sql >> Databáze >  >> NoSQL >> MongoDB

Agregační dotaz mongodb nevrací správný součet při použití $sum

Vaše aktuální schéma má marks datový typ pole jako řetězec a pro výpočet součtu potřebujete celočíselný datový typ pro váš agregační rámec. Na druhou stranu můžete použít MapReduce pro výpočet součtu, protože umožňuje použití nativních metod JavaScriptu, jako je parseInt() na vlastnosti vašeho objektu v jeho mapových funkcích. Celkově tedy máte dvě možnosti.

Možnost 1:Aktualizace schématu (změna datového typu)

První by bylo změnit schéma nebo přidat další pole do dokumentu, které má skutečnou číselnou hodnotu, nikoli reprezentaci řetězce. Pokud je velikost vašeho sbírkového dokumentu relativně malá, můžete použít kombinaci kurzoru mongodb find() , forEach() a update() metody pro změnu schématu známek:

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

U relativně velkých velikostí kolekce bude výkon vaší databáze pomalý a doporučuje se použít hromadné aktualizace mongo pro toto:

Verze MongoDB>=2.6 a <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 verze 3.2 a novější:

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

Možnost 2:Spusťte MapReduce

Druhým přístupem by bylo přepsat dotaz pomocí MapReduce kde můžete použít funkci JavaScriptu parseInt() .

Ve vašem MapReduce operace, definovat mapové funkce, které zpracovávají každý vstupní dokument. Tato funkce mapuje převedené marks hodnotu řetězce do subject pro každý dokument a vydá subject a převedené marks pár. Zde je nativní funkce JavaScriptu parseInt() lze aplikovat. Poznámka:ve funkci this odkazuje na dokument, který operace zmenšení mapy zpracovává:

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

Dále definujte odpovídající funkci snížení pomocí dvou argumentů keySubject a valuesMarks . valuesMarks je pole, jehož prvky jsou celé číslo marks hodnoty emitované funkcí mapy a seskupené podle keySubject .Funkce snižuje valuesMarks pole k součtu jeho prvků.

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

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

S vaší sbírkou výše uvedené umístí váš výsledek agregace MapReduce do nové sbírky db.example_results . Tedy db.example_results.find() vypíše:

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


  1. MongoDB Java Driver:MongoCore Driver vs. MongoDB Driver vs. MongoDB Async Driver

  2. Aktualizovat dokument MongoEngine pomocí pythonského diktátu?

  3. MongoDB:aktualizujte pouze konkrétní pole

  4. Jak získám číslo verze MongoDB na Heroku?