sql >> Database >  >> NoSQL >> MongoDB

mongodb aggregatiequery retourneert geen juiste som bij gebruik van $sum

Uw huidige schema heeft de marks veldgegevenstype als tekenreeks en u hebt een gegevenstype met een geheel getal nodig voor uw aggregatieraamwerk om de som uit te werken. Aan de andere kant kun je MapReduce gebruiken om de som te berekenen, aangezien het het gebruik van native JavaScript-methoden zoals parseInt() . toestaat op uw objecteigenschappen in zijn kaartfuncties. Dus over het algemeen heb je twee keuzes.

Optie 1:schema bijwerken (gegevenstype wijzigen)

De eerste zou zijn om het schema te wijzigen of een ander veld in uw document toe te voegen dat de werkelijke numerieke waarde heeft, niet de tekenreeksrepresentatie. Als uw collectiedocument relatief klein is, kunt u een combinatie van de mongodb-cursor find() gebruiken , forEach() en update() methoden om uw cijferschema te wijzigen:

db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
    db.student.update(
        { "_id": doc._id, "marks": { "$type": 2 } }, 
        { "$set": { "marks": parseInt(doc.marks) } }
    );
});

Voor relatief grote collecties zullen uw db-prestaties traag zijn en het wordt aanbevolen om mongo bulkupdates hiervoor:

MongoDB-versies>=2.6 en <3.2:

var bulk = db.student.initializeUnorderedBulkOp(),
    counter = 0;

db.student.find({"marks": {"$exists": true, "$type": 2 }}).forEach(function (doc) {    
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$set": { "marks": parseInt(doc.marks) } 
    });

    counter++;
    if (counter % 1000 === 0) {
        // Execute per 1000 operations 
        bulk.execute(); 

        // re-initialize every 1000 update statements
        bulk = db.student.initializeUnorderedBulkOp();
    }
})

// Clean up remaining operations in queue
if (counter % 1000 !== 0) bulk.execute(); 

MongoDB versie 3.2 en nieuwer:

var ops = [],
    cursor = db.student.find({"marks": {"$exists": true, "$type": 2 }});

cursor.forEach(function (doc) {     
    ops.push({ 
        "updateOne": { 
            "filter": { "_id": doc._id } ,              
            "update": { "$set": { "marks": parseInt(doc.marks) } } 
        }         
    });

    if (ops.length === 1000) {
        db.student.bulkWrite(ops);
        ops = [];
    }     
});

if (ops.length > 0) db.student.bulkWrite(ops);

Optie 2:MapReduce uitvoeren

De tweede benadering zou zijn om uw zoekopdracht te herschrijven met MapReduce waar u de JavaScript-functie parseInt() . kunt gebruiken .

In uw MapReduce bewerking, definieert u de kaartfunctie die elk invoerdocument verwerkt. Deze functie wijst de geconverteerde marks toe tekenreekswaarde aan het subject voor elk document, en zendt het subject . uit en geconverteerde marks paar. Dit is waar de JavaScript-native functie parseInt() kan worden toegepast. Opmerking:in de functie, this verwijst naar het document dat de bewerking voor het verkleinen van de kaart aan het verwerken is:

var mapper = function () {
    var x = parseInt(this.marks);
    emit(this.subject, x);
};

Definieer vervolgens de corresponderende reduceerfunctie met twee argumenten keySubject en valuesMarks . valuesMarks is een array waarvan de elementen de integer marks . zijn waarden uitgezonden door de kaartfunctie en gegroepeerd op keySubject .De functie reduceert de valuesMarks array tot de som van de elementen.

var reducer = function(keySubject, valuesMarks) {
    return Array.sum(valuesMarks);
};

db.student.mapReduce(
    mapper,
    reducer,
    {
        out : "example_results",
        query: { subject : "maths" }       
    }
 );

Met uw verzameling zet het bovenstaande uw MapReduce-aggregatieresultaat in een nieuwe verzameling db.example_results . Dus db.example_results.find() zal uitvoeren:

/* 0 */
{
    "_id" : "maths",
    "value" : 163
}


  1. Kan MongoDB een index gebruiken bij het controleren op het bestaan ​​van een veld met de operator $exists?

  2. MongoDB $verdelen

  3. voeg meerdere documenten samen tot één document met beide documentvelden in MongoDB

  4. Databasestatuscontrole automatiseren