如何在mongodb中将字符串转换为数值

时间:2015-04-07 08:41:09

标签: mongodb mongodb-query

我正在尝试将包含数值的字符串转换为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) 
    }
}

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

13 个答案:

答案 0 :(得分:26)

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

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

如果您的集合大小超过以上脚本会降低性能,对于perfomace 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个排队操作只发送一次。

答案 1 :(得分:14)

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

不要忘记更改collectionName&字段名。 例如:CollectionNmae:Users& FieldName:Contactno。

尝试此查询..

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

答案 2 :(得分:10)

最终我用了

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从字符串转换为整数。

答案 3 :(得分:9)

使用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,您可以使用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是一个数组,其元素是map函数发出的整数moop值,并按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
}

答案 4 :(得分:6)

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

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

输出:

{ "moopNumber" : 1234 }

答案 5 :(得分:4)

这里是一个纯粹的基于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 }

答案 6 :(得分:2)

需要关注三件事:

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

答案 7 :(得分:1)

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

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

答案 8 :(得分:1)

整理是您所需要的:

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

答案 9 :(得分:0)

尝试:

<xsl:variable name="lookup" select="('documentFile')/>  . 

答案 10 :(得分:0)

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

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

然后为客户端设置查询:

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

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

答案 11 :(得分:-1)

尽管$toInt确实很有用,但它是在mongoDB 4.0上添加的,但是在运行3.2的数据库中也遇到了同样的情况,由于其他原因,无法升级为使用$toInt应用程序不兼容,因此我不得不提出其他建议,而且实际上非常简单。

如果您将$project$add设为零,则字符串将变成数字

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

答案 12 :(得分:-2)

  

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