【问题标题】:how to convert string to numerical values in mongodb如何在mongodb中将字符串转换为数值
【发布时间】:2015-06-11 19:11:38
【问题描述】:

我正在尝试将包含数值的字符串转换为其在 MongoDB 中的聚合查询中的值。

文档示例

{
"_id": ObjectId("5522XXXXXXXXXXXX"),
   "Date": "2015-04-05",
   "PartnerID": "123456",
   "moop": "1234" 
}

我使用的聚合查询示例

{
    aggregate: 'my_collection',
    pipeline: [
         {$match: {
             Date : 
                  {$gt:'2015-04-01', 
                  $lt: '2015-04-05'
                  }}
             },
         {$group:
             {_id: "$PartnerID",
              total:{$sum:'$moop'}
             }}]}

结果在哪里

{
   "result": [
     {
       "_id": "123456",
       "total": NumberInt(0) 
    }
}

如何将字符串转换为数值?

【问题讨论】:

标签: mongodb mongodb-query


【解决方案1】:

虽然$toInt 非常有用,但它是在 mongoDB 4.0 上添加的,但我在运行 3.2 的数据库中遇到了同样的情况,由于其他一些应用程序不兼容,升级到使用 $toInt 不是一个选项,所以我不得不想出别的办法,而且实际上非常简单。

如果你 $project$add 将你的字符串归零,它将变成一个数字

{
  $project : {
  'convertedField' : { $add : ["$stringField",0] },
  //more fields here...
  }
}

【讨论】:

  • mongodb ver 3.6.18 , "message" : "$add 只支持数字或日期类型,不支持字符串",
  • @gvasquez 我想你错过了它说我运行的是 3.2,而不是 3.6 的部分。与此同时,可能发生了一些变化。
【解决方案2】:

如果您可以汇总编辑所有文档:

"TimeStamp": {$toDecimal: {$toDate: "$Your Date"}}

对于客户端,您设置查询:

Date.parse("Your date".toISOString())

这就是让您完全使用 ISODate 的原因。

【讨论】:

    【解决方案3】:

    试试:

    "TimeStamp":{$toDecimal: { $toDate:"$Datum"}}
    

    【讨论】:

      【解决方案4】:

      最后我用了

      db.my_collection.find({moop: {$exists: true}}).forEach(function(obj) {
          obj.moop = new NumberInt(obj.moop);
          db.my_collection.save(obj);
      });
      

      按照 Simone 的回答 MongoDB: How to change the type of a field? 中的示例,将 my_collection 中的 moop 从字符串转换为整数。

      【讨论】:

      • 对数组进行交互需要很长时间。
      【解决方案5】:

      您可以轻松地将字符串数据类型转换为数值数据类型。

      不要忘记更改 collectionName 和 FieldName。 例如:CollectionNmae:用户和字段名称:联系人号码。

      试试这个查询..

      db.collectionName.find().forEach( function (x) {
      x.FieldName = parseInt(x.FieldName);
      db.collectionName.save(x);
      });
      

      【讨论】:

        【解决方案6】:

        这是一个纯粹的基于 MongoDB 的解决方案,我只是为了好玩而写的。它实际上是一个服务器端字符串到数字的解析器,支持正数和负数以及小数:

        db.collection.aggregate({
            $addFields: {
                "moop": {
                    $reduce: {
                        "input": {
                            $map: { // split string into char array so we can loop over individual characters
                                "input": {
                                    $range: [ 0, { $strLenCP: "$moop" } ] // using an array of all numbers from 0 to the length of the string
                                },
                                "in":{
                                    $substrCP: [ "$moop", "$$this", 1 ] // return the nth character as the mapped value for the current index
                                }
                            }
                        },
                        "initialValue": { // initialize the parser with a 0 value
                            "n": 0, // the current number
                            "sign": 1, // used for positive/negative numbers
                            "div": null, // used for shifting on the right side of the decimal separator "."
                            "mult": 10 // used for shifting on the left side of the decimal separator "."
                        }, // start with a zero
                        "in": {
                            $let: {
                                "vars": {
                                    "n": {
                                        $switch: { // char-to-number mapping
                                            branches: [
                                                { "case": { $eq: [ "$$this", "1" ] }, "then": 1 },
                                                { "case": { $eq: [ "$$this", "2" ] }, "then": 2 },
                                                { "case": { $eq: [ "$$this", "3" ] }, "then": 3 },
                                                { "case": { $eq: [ "$$this", "4" ] }, "then": 4 },
                                                { "case": { $eq: [ "$$this", "5" ] }, "then": 5 },
                                                { "case": { $eq: [ "$$this", "6" ] }, "then": 6 },
                                                { "case": { $eq: [ "$$this", "7" ] }, "then": 7 },
                                                { "case": { $eq: [ "$$this", "8" ] }, "then": 8 },
                                                { "case": { $eq: [ "$$this", "9" ] }, "then": 9 },
                                                { "case": { $eq: [ "$$this", "0" ] }, "then": 0 },
                                                { "case": { $and: [ { $eq: [ "$$this", "-" ] }, { $eq: [ "$$value.n", 0 ] } ] }, "then": "-" }, // we allow a minus sign at the start
                                                { "case": { $eq: [ "$$this", "." ] }, "then": "." }
                                            ],
                                            default: null // marker to skip the current character
                                        } 
                                    }
                                },
                                "in": {
                                    $switch: {
                                        "branches": [
                                            {
                                                "case": { $eq: [ "$$n", "-" ] },
                                                "then": { // handle negative numbers
                                                    "sign": -1, // set sign to -1, the rest stays untouched
                                                    "n": "$$value.n",
                                                    "div": "$$value.div",
                                                    "mult": "$$value.mult",
                                                },
                                            },
                                            {
                                                "case": { $eq: [ "$$n", null ] }, // null is the "ignore this character" marker
                                                "then": "$$value" // no change to current value
                                            }, 
                                            {
                                                "case": { $eq: [ "$$n", "." ] },
                                                "then": { // handle decimals
                                                    "n": "$$value.n",
                                                    "sign": "$$value.sign",
                                                    "div": 10, // from the decimal separator "." onwards, we start dividing new numbers by some divisor which starts at 10 initially
                                                    "mult": 1, // and we stop multiplying the current value by ten
                                                },
                                            }, 
                                        ],
                                        "default": {
                                            "n": {
                                                $add: [
                                                    { $multiply: [ "$$value.n", "$$value.mult" ] }, // multiply the already parsed number by 10 because we're moving one step to the right or by one once we're hitting the decimals section
                                                    { $divide: [ "$$n", { $ifNull: [ "$$value.div", 1 ] } ] } // add the respective numerical value of what we look at currently, potentially divided by a divisor
                                                ]
                                            },
                                            "sign": "$$value.sign",
                                            "div": { $multiply: [ "$$value.div" , 10 ] },
                                            "mult": "$$value.mult"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }, {
            $addFields: { // fix sign
                "moop": { $multiply: [ "$moop.n", "$moop.sign" ] }
            }
        })
        

        我当然不会将其宣传为蜜蜂的膝盖或任何东西,它可能会对基于客户端的解决方案的更大数据集产生严重的性能影响,但在某些情况下它可能会派上用场......

        上述管道将转换以下文档:

        { "moop": "12345" } --> { "moop": 12345 }
        

        { "moop": "123.45" } --> { "moop": 123.45 }
        

        { "moop": "-123.45" } --> { "moop": -123.45 }
        

        { "moop": "2018-01-03" } --> { "moop": 20180103.0 }
        

        【讨论】:

        • 这是低于 4.0 版本的唯一 mongo 方法
        • 这肯定是我唯一写过/能想到的。而且,老实说,升级是一个更好的选择。
        • 这至少是一个很棒的练习——做得好!
        【解决方案7】:

        使用 MongoDB 4.0 及更新版本

        您有两个选项,即 $toInt$convert。使用 $toInt,遵循以下示例:

        filterDateStage = {
            '$match': {
                'Date': {
                    '$gt': '2015-04-01', 
                    '$lt': '2015-04-05'
                }
            }
        };
        
        groupStage = {
            '$group': {
                '_id': '$PartnerID',
                'total': { '$sum': { '$toInt': '$moop' } }
            }
        };
        
        db.getCollection('my_collection').aggregate([
           filterDateStage,
           groupStage
        ])
        

        如果转换操作遇到错误,聚合操作将停止并抛出错误。要覆盖此行为,请改用 $convert

        使用 $convert

        groupStage = {
            '$group': {
                '_id': '$PartnerID',
                'total': { 
                    '$sum': { 
                        '$convert': { 'input': '$moop', 'to': 'int' }
                    } 
                }
            }
        };
        

        使用 Map/Reduce

        借助 map/reduce,您可以使用 parseInt() 等 JavaScript 函数进行转换。例如,您可以定义 map 函数来处理每个输入文档: 在函数中,this 指的是 map-reduce 操作正在处理的文档。该函数将转换后的moop 字符串值映射到每个文档的PartnerID,并发出PartnerID 和转换后的moop 对。这是可以应用javascript原生函数parseInt()的地方:

        var mapper = function () {
            var x = parseInt(this.moop);
            emit(this.PartnerID, x);
        };
        

        接下来,用两个参数keyCustIdvaluesMoop 定义相应的reduce 函数。 valuesMoop 是一个数组,其元素是整数 moop 值,由 map 函数发出并按 keyPartnerID 分组。 该函数将valuesMoop 数组减少为其元素的总和。

        var reducer = function(keyPartnerID, valuesMoop) {
                          return Array.sum(valuesMoop);
                      };
        
        db.collection.mapReduce(
            mapper,
            reducer,
            {
                out : "example_results",
                query: { 
                    Date: {
                        $gt: "2015-04-01", 
                        $lt: "2015-04-05"
                    }
                }       
            }
         );
        
         db.example_results.find(function (err, docs) {
            if(err) console.log(err);
            console.log(JSON.stringify(docs));
         });
        

        例如,使用以下示例文档集合:

        /* 0 */
        {
            "_id" : ObjectId("550c00f81bcc15211016699b"),
            "Date" : "2015-04-04",
            "PartnerID" : "123456",
            "moop" : "1234"
        }
        
        /* 1 */
        {
            "_id" : ObjectId("550c00f81bcc15211016699c"),
            "Date" : "2015-04-03",
            "PartnerID" : "123456",
            "moop" : "24"
        }
        
        /* 2 */
        {
            "_id" : ObjectId("550c00f81bcc15211016699d"),
            "Date" : "2015-04-02",
            "PartnerID" : "123457",
            "moop" : "21"
        }
        
        /* 3 */
        {
            "_id" : ObjectId("550c00f81bcc15211016699e"),
            "Date" : "2015-04-02",
            "PartnerID" : "123457",
            "moop" : "8"
        }
        

        上面的 Map/Reduce 操作会将结果保存到example_results 集合中,shell 命令db.example_results.find() 会给出:

        /* 0 */
        {
            "_id" : "123456",
            "value" : 1258
        }
        
        /* 1 */
        {
            "_id" : "123457",
            "value" : 29
        }
        

        【讨论】:

          【解决方案8】:

          db.user.find().toArray().filter(a=>a.age>40)

          【讨论】:

            【解决方案9】:

            在 MongoDB v4.0 中,可以使用 $toInt 运算符将字符串转换为数字。在这种情况下

            db.col.aggregate([
                {
                    $project: {
                        _id: 0,
                        moopNumber: { $toInt: "$moop" }
                    }
                }
            ])
            

            输出:

            { "moopNumber" : 1234 }
            

            【讨论】:

              【解决方案10】:

              整理是您所需要的:

              db.collectionName.find().sort({PartnerID: 1}).collation({locale: "en_US", numericOrdering: true})
              

              【讨论】:

                【解决方案11】:

                它应该被保存。应该是这样的:

                     db. my_collection.find({}).forEach(function(theCollection) {
                         theCollection.moop = parseInt(theCollection.moop);
                        db.my_collection.save(theCollection);
                     });
                

                【讨论】:

                  【解决方案12】:

                  需要注意三件事:

                  1. parseInt() 将在 mongodb 中存储双精度数据类型。请使用新的 NumberInt(string)。
                  2. 在用于批量使用的 Mongo shell 命令中,yield 不起作用。请不要添加“产量”。
                  3. 如果您已经通过 parseInt() 将字符串更改为双精度。看起来您无法直接将类型更改为 int 。解决方案有点复杂:先将 double 更改为 string,然后通过 new NumberInt() 更改回 int。

                  【讨论】:

                  • 不,ParseInt 从不存储为 double,而是 int32。
                  【解决方案13】:

                  MongoDB 聚合不允许更改给定字段的现有数据类型。在这种情况下,您应该创建一些编程代码来将string 转换为int。检查下面的代码

                  db.collectionName.find().forEach(function(data) {
                      db.collectionName.update({
                          "_id": data._id,
                          "moop": data.moop
                      }, {
                          "$set": {
                              "PartnerID": parseInt(data.PartnerID)
                          }
                      });
                  })
                  

                  如果您的集合大小超过上述脚本会降低性能,对于性能 mongo 提供 mongo bulk 操作,使用 mongo 批量操作也会更新数据类型

                  var bulk = db.collectionName.initializeOrderedBulkOp();
                  var counter = 0;
                  db.collectionName.find().forEach(function(data) {
                      var updoc = {
                          "$set": {}
                      };
                      var myKey = "PartnerID";
                      updoc["$set"][myKey] = parseInt(data.PartnerID);
                      // queue the update
                      bulk.find({
                          "_id": data._id
                      }).update(updoc);
                      counter++;
                      // Drain and re-initialize every 1000 update statements
                      if (counter % 1000 == 0) {
                          bulk.execute();
                          bulk = db.collectionName.initializeOrderedBulkOp();
                      }
                      })
                      // Add the rest in the queue
                  if (counter % 1000 != 0) bulk.execute();
                  

                  这基本上将发送到服务器的操作语句数量减少到每 1000 个排队操作只发送一次。

                  【讨论】:

                  • 谢谢,使用了另一种方法,如下所述。
                  • 迟到了,但bulk.execute(),如果不带参数调用,返回Promise。在我开始将它视为Promise 之前,您的示例对我不起作用。在我使用co 的情况下,我只是在它前面添加了yield
                  • 你能帮忙解决这个问题吗? stackoverflow.com/questions/61165765/…
                  猜你喜欢
                  • 1970-01-01
                  • 2015-06-12
                  • 1970-01-01
                  • 2022-01-02
                  • 2014-10-30
                  • 1970-01-01
                  • 2022-01-23
                  • 2020-02-05
                  • 1970-01-01
                  相关资源
                  最近更新 更多