MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Funzioni personalizzate calcolate colonne mongodb proiezione

Sembri pensare che sia possibile chiamare una funzione JavaScript nella pipeline di aggregazione, ma non puoi farlo. Stai confondendo ciò che è effettivamente "interpolazione" di una variabile dal risultato di una funzione per l'esecuzione all'interno della pipeline.

Ad esempio, se lo faccio:

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

Quindi chiamo questo:

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

Quindi ciò che effettivamente accade nella shell JavaScript i valori vengono "interpolati" e "prima" che l'istruzione venga inviata al server, in questo modo:

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

Per dimostrarlo ulteriormente, memorizza una funzione "solo" sul server:

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

Quindi prova a eseguire l'istruzione di aggregazione:

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

E ciò comporterà un'eccezione:

E QUERY [main] ReferenceError:hello non è definito in (shell):1:69

Questo perché l'esecuzione sta avvenendo sul "client" e non sul "server" e la funzione non esiste sul client.

Il framework di aggregazione non può eseguire JavaScript, poiché non dispone di alcuna disposizione per farlo. Tutte le operazioni vengono eseguite in codice nativo, senza che venga invocato alcun motore JavaScript. Quindi usi invece gli operatori lì:

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

Se non è possibile utilizzare gli operatori per ottenere i risultati, l'unico modo per eseguire il codice JavaScript è eseguire invece mapReduce, che ovviamente utilizza un motore JavaScript per interfacciarsi con i dati della raccolta. E da lì puoi anche fare riferimento a una funzione lato server all'interno della tua logica se necessario:

{ "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 } }
)

Resi:

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

Quindi non si tratta di "come passare un valore di campo" ma in realtà del fatto che il framework di aggregazione non supporta in alcun modo JavaScript e che ciò che pensavi stesse accadendo in realtà non è il caso.