MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Benutzerdefinierte Funktionen berechneten Spalten Mongodb-Projektion

Sie scheinen zu glauben, dass es möglich ist, eine JavaScript-Funktion in der Aggregationspipeline aufzurufen, aber Sie können dies nicht tun. Sie verwechseln, was eigentlich die "Interpolation" einer Variablen aus einem Funktionsergebnis für die Ausführung innerhalb der Pipeline ist.

Zum Beispiel, wenn ich dies tue:

var getNumbers = function() { return [ 1,2,3 ] };

Dann nenne ich das:

db.collection.aggregate([
    { "$project": {
        "mynums": getNumbers()
    }}  
])

Was dann tatsächlich in der JavaScript-Shell passiert, die Werte werden "interpoliert" und "bevor" die Anweisung an den Server gesendet wird, wie folgt:

db.collection.aggregate([
    { "$project": {
        "mynums": [1,2,3]
    }}  
])

Um dies weiter zu demonstrieren, speichern Sie eine Funktion "nur" auf dem Server:

db.system.js.save({ "_id": "hello", "value": function() { return "hello" } })

Versuchen Sie dann, die Aggregationsanweisung auszuführen:

db.collection.aggregate([
    { "$project": {
        "greeting": hello()
    }}  
])

Und das führt zu einer Ausnahme:

E QUERY [main] ReferenceError:hello is not defined at (shell):1:69

Das liegt daran, dass die Ausführung auf dem „Client“ und nicht auf dem „Server“ stattfindet und die Funktion auf dem Client nicht vorhanden ist.

Das Aggregations-Framework kann das nicht Führen Sie JavaScript aus, da dies nicht vorgesehen ist. Alle Operationen werden in nativem Code ausgeführt, ohne dass eine JavaScript-Engine aufgerufen wird. Verwenden Sie daher stattdessen die dortigen Operatoren:

db.collection.aggregate([
    { "$project": {
        "total": { "$add": [ 1, 2 ] },
        "field_total": { "$subtract": [ "$gross", "$tax" ] }
    }}  
])   

Wenn Sie die Operatoren nicht verwenden können, um die Ergebnisse zu erzielen, können Sie JavaScript-Code nur ausführen, indem Sie stattdessen mapReduce ausführen, das natürlich eine JavaScript-Engine verwendet, um mit den Daten aus der Sammlung zu kommunizieren. Und von dort aus können Sie bei Bedarf auch auf eine serverseitige Funktion in Ihrer Logik verweisen:

{ "key": 1, "value": 1 },
{ "key": 1, "value": 2 },
{ "key": 1, "value": 3 }

db.system.js.save({ "_id": "square", "value": function(num) { return num * num } })

db.collection.mapReduce(
    function() {
        emit(this.key,square(this.value))
    },
    function(key,values) {
        return Array.sum(values);
    },
    { "out": { "inline": 1 } }
)

Rückgabe:

{
    "_id": 1,
    "value": 14
}

Hier geht es also nicht darum, "wie man einen Feldwert übergibt", sondern wirklich darum, dass das Aggregations-Framework JavaScript in keiner Weise unterstützt und dass das, was Sie dachten, tatsächlich nicht der Fall ist.