web-dev-qa-db-fra.com

comment convertir une chaîne en valeurs numériques dans mongodb

J'essaie de convertir une chaîne contenant une valeur numérique en valeur dans une requête agrégée dans MongoDB.

Exemple de document

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

Exemple de requête globale que j'utilise

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

où les résultats sont 

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

Comment pouvez-vous convertir la chaîne en valeur numérique?

28
Naftsen

L'agrégation MongoDB n'est pas autorisée à modifier le type de données existant de champs donnés. Dans ce cas, vous devez créer du code de programmation pour convertir string en int. Vérifiez ci-dessous le code 

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

Si la taille de vos collections dépasse le script ci-dessus, les performances ralentissent. Pour perfomace mongo, fournissez les opérations mongo bulk , à l'aide du type de données mis à jour à l'aide d'opérations mongo bulk.

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();

Cela réduit essentiellement le nombre d'instructions d'opération envoyées au serveur pour n'envoyer qu'une fois toutes les 1 000 opérations en file d'attente. 

26
Yogesh

Vous pouvez facilement convertir le type de données string en type de données numérique.

N'oubliez pas de changer collectionName & FieldName . Par exemple: CollectionNmae: Users & FieldName: Contactno.

Essayez cette requête ..

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

Finalement, j'ai utilisé 

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

transformer moop de chaîne en entier dans my_collection en suivant l'exemple de la réponse de Simone MongoDB: Comment changer le type d'un champ? .

9
Naftsen

Utilisation de MongoDB 4.0 et plus récent

Vous avez le choix entre deux options: $toInt ou $convert. En utilisant $toInt, suivez l'exemple ci-dessous:

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
])

Si l'opération de conversion rencontre une erreur, l'opération d'agrégation s'arrête et génère une erreur. Pour remplacer ce comportement, utilisez plutôt $convert.

Utilisation de $convert

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

Utilisation de la carte/réduction

Avec map/reduction, vous pouvez utiliser des fonctions javascript telles que parseInt() pour effectuer la conversion. À titre d'exemple, vous pouvez définir la fonction de carte pour traiter chaque document d'entrée: Dans la fonction, this fait référence au document que l'opération de réduction de carte est en train de traiter. La fonction mappe la valeur de chaîne moop convertie en PartnerID pour chaque document et émet la paire PartnerID et convertie moop. C’est ici que la fonction native javascript parseInt() peut être appliquée:

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

Ensuite, définissez la fonction de réduction correspondante avec deux arguments keyCustId et valuesMoop. valuesMoop est un tableau dont les éléments sont les valeurs entières moop émises par la fonction map et regroupées par keyPartnerID. La fonction réduit le tableau valuesMoop à la somme de ses éléments.

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));
 });

Par exemple, avec l'exemple de collection de documents suivant:

/* 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"
}

L'opération Carte/Réduction ci-dessus enregistrera les résultats dans la collection example_results et la commande Shell db.example_results.find() donnera:

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

/* 1 */
{
    "_id" : "123457",
    "value" : 29
}
6
chridam

Voici une solution basée sur MongoDB pour résoudre ce problème que je viens d’écrire pour le plaisir. Il s’agit d’un analyseur chaîne-nombre côté serveur qui prend en charge les nombres positifs et négatifs, ainsi que les nombres décimaux:

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" ] }
    }
})

Je ne fais certainement pas cette publicité comme étant le genou de l'abeille ou quoi que ce soit et cela pourrait avoir de graves conséquences sur les performances pour des jeux de données plus volumineux par rapport à des solutions basées sur le client, mais il peut arriver que cela soit utile ...

Le pipeline ci-dessus transformera les documents suivants:

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

et

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

et

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

et

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

La chaîne peut être convertie en nombres dans MongoDB v4.0 en utilisant $ toInt operator. Dans ce cas

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

les sorties:

{ "moopNumber" : 1234 }
4
mickl

Trois choses doivent prendre en compte:

  1. parseInt () stockera le type de données double dans mongodb. Veuillez utiliser new NumberInt (string).
  2. dans la commande Mongo Shell pour une utilisation en vrac, le rendement ne fonctionnera pas. S'il vous plaît NE PAS ajouter «rendement».
  3. Si vous changez déjà de chaîne en doublant par parseInt (). Il semble que vous n’ayez aucun moyen de changer le type en int directement. La solution est un peu câblée: changez double en chaîne d'abord, puis revenez à int avec new NumberInt ().
3
Bing Wu

La collation est ce dont vous avez besoin:

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

Cela devrait être sauvé. Cela devrait être comme ça:

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

Essayer:

"TimeStamp":{$toDecimal: { $toDate:"$Datum"}}
0
Dalibor Adamec

Si vous pouvez éditer tous les documents ensemble:

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

Et pour le client, vous définissez la requête:

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

C'est ce qui vous fait travailler avec ISODate.

0
Dalibor Adamec