MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

MongoDB calcule le score à partir des champs existants et le place dans un nouveau champ de la même collection

Selon les besoins de votre application, vous pouvez utiliser le cadre d'agrégation pour calculer le score et utiliser le bulkWrite() pour mettre à jour votre collection. Prenons l'exemple suivant qui utilise le $project étape du pipeline comme marge de manœuvre pour les calculs de score avec les opérateurs arithmétiques.

Puisque la logique de calcul de C3 dans votre question obtient un nombre de 1 à 7 qui est exactement égal à 7 - number of points (.) , la seule approche réalisable à laquelle je peux penser est de stocker un champ supplémentaire contenant cette valeur avant de procéder à l'agrégation. Donc, votre première étape serait de créer ce champ supplémentaire et vous pouvez le faire en utilisant le bulkWrite() comme suit :

Étape 1 :Modifier le schéma pour accepter des daysInWeek supplémentaires champ

var counter = 0, bulkUpdateOps = [];

db.collection1.find({
    "Field5": { "$exists": true }
}).forEach(function(doc) {
    // calculations for getting the number of points in Field5
    var points, daysInWeek;
    points = (doc.Field5.match(new RegExp(".", "g")) || []).length;
    daysInWeek = 7 - points;
    bulkUpdateOps.push({
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": {
                "$set": { "daysInWeek": daysInWeek }
            }
        }
    });
    counter++;

    if (counter % 500 == 0) {
        db.collection1.bulkWrite(bulkUpdateOps);
        bulkUpdateOps = [];
    }
});

if (counter % 500 != 0) { db.collection1.bulkWrite(bulkUpdateOps); }

Idéalement, l'opération ci-dessus peut également permettre de calculer les autres constantes de votre question et donc de créer le Field8 par conséquent. Cependant, je pense que des calculs comme celui-ci devraient être effectués sur le client et laisser MongoDB faire ce qu'il fait le mieux sur le serveur.

Étape 2 :Utilisez l'agrégation pour ajouter Field8 champ

Après avoir créé ce champ supplémentaire daysInWeek vous pouvez ensuite construire un pipeline d'agrégation qui projette les nouvelles variables à l'aide d'une cohorte de opérateurs arithmétiques pour faire le calcul (encore une fois, je recommanderais de faire de tels calculs sur la couche application). La projection finale sera le produit des champs calculés que vous pourrez ensuite utiliser le curseur de résultat agrégé pour itérer et ajouter Field8 à la collection avec chaque document :

var pipeline = [
        {
            "$project": {
                "C1": {
                    "$add": [ 
                        10, 
                        { "$multiply": [ "$Field3", 0.03 ] } 
                    ]
                },
                "C2": {
                    "$cond": [
                        { "$eq": [ "$Field2", 1 ] }, 
                        1, 
                        0.03 
                    ]
                },
                "C3": "$daysInWeek",
                "C4": {
                    "$cond": [
                        { "$eq": [ "$Field2", 1 ]  },
                        { "$pow": [ "$Field4", -0.6 ] },
                        1
                    ]
                }
            }
        },
        {
            "$project": {
                "Field8": { "$multiply": [ "$C1", "$C2", "$C3", "$C4" ] }
            }
        }
    ],
    counter = 0,
    bulkUpdateOps = [];

db.collection1.aggregate(pipeline).forEach(function(doc) {
    bulkUpdateOps.push({
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": {
                "$set": { "Field8": doc.Field8 }
            }
        }
    });
    counter++;

    if (counter % 500 == 0) {
        db.collection1.bulkWrite(bulkUpdateOps);
        bulkUpdateOps = [];
    }
});

if (counter % 500 != 0) { db.collection1.bulkWrite(bulkUpdateOps); }

Pour MongoDB >= 2.6 et <= 3.0 , utilisez l'l'API des opérations en masse où vous devez itérer la collection en utilisant le forEach() , mettez à jour chaque document de la collection.

Certains des opérateurs arithmétiques du pipeline d'agrégation ci-dessus ne sont pas disponibles dans MongoDB >= 2.6 et <= 3.0 vous devrez donc effectuer les calculs dans le forEach() itération.

Utilisez l'API en bloc pour réduire les demandes d'écriture du serveur en regroupant chaque mise à jour en bloc et en envoyant au serveur une seule fois tous les 500 documents de la collection pour traitement :

var bulkUpdateOps = db.collection1.initializeUnorderedBulkOp(),
    cursor = db.collection1.find(), // cursor 
    counter = 0;

cursor.forEach(function(doc) {
    // computations
    var c1, c2, c3, c4, Field8;
    c1 = 10 + (0.03*doc.Field3);
    c2 = (doc.Field2 == 1) ? 1: 0.03;
    c3 = 7 - (doc.Field5.match(new RegExp(".", "g")) || []).length;
    c4 = (doc.Field2 == 1) ? Math.pow(doc.Field, -0.6) : 1;
    Field8 = c1*c2*c3*c4;

    bulkUpdateOps.find({ "_id": doc._id }).updateOne({
        "$set": { "Field8": Field8 }
    });

    if (counter % 500 == 0) {
        bulkUpdateOps.execute();
        bulkUpdateOps = db.collection1.initializeUnorderedBulkOp();
    }
})

if (counter % 500 != 0) { bulkUpdateOps.execute(); }