MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

MongoDB calculando a pontuação de campos existentes e colocando-a em um novo campo na mesma coleção


Dependendo das necessidades do seu aplicativo, você pode usar a estrutura de agregação para calcular a pontuação e usar o bulkWrite() para atualizar sua coleção. Considere o exemplo a seguir que usa o $project passo do pipeline como margem de manobra para os cálculos de pontuação com os operadores aritméticos.

Desde lógica para calcular C3 na sua pergunta está recebendo um número de 1 para 7 que equivale exatamente a 7 - number of points (.) , a única abordagem viável que consigo pensar é armazenar um campo extra que retenha esse valor antes de fazer a agregação. Portanto, seu primeiro passo seria criar esse campo extra e você pode fazer isso usando o bulkWrite() do seguinte modo:

Etapa 1:modifique o esquema para acomodar mais daysInWeek campo
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); }

Idealmente, a operação acima também pode acomodar o cálculo das outras constantes em sua pergunta e, portanto, criar o Field8 como resultado. No entanto, acredito que cálculos como esse devem ser feitos no cliente e deixar o MongoDB fazer o que faz de melhor no servidor.

Etapa 2:use a agregação para adicionar Field8 campo

Tendo criado esse campo extra daysInWeek você pode então construir um pipeline de agregação que projeta as novas variáveis ​​usando uma coorte de operadores aritméticos para fazer o cálculo (novamente, recomendaria fazer tais cálculos na camada de aplicação). A projeção final será o produto dos campos calculados que você pode usar o cursor de resultado agregado para iterar e adicionar Field8 à coleção com cada documento:
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); }

Para MongoDB >= 2.6 e <= 3.0 , use a API de operações em massa onde você precisa iterar a coleção usando o forEach() método, atualize cada documento na coleção.

Alguns dos operadores aritméticos do pipeline de agregação acima não estão disponíveis no MongoDB >= 2.6 e <= 3.0 então você precisará fazer os cálculos dentro do forEach() iteração.

Use a API em massa para reduzir as solicitações de gravação do servidor agrupando cada atualização em massa e enviando ao servidor apenas uma vez a cada 500 documentos na coleção para processamento:
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(); }