MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Fonctions personnalisées colonnes calculées projection mongodb

Vous semblez penser qu'il est possible d'appeler une fonction JavaScript dans le pipeline d'agrégation, mais vous ne pouvez pas le faire. Vous confondez ce qui est réellement "l'interpolation" d'une variable à partir d'un résultat de fonction pour l'exécution dans le pipeline.

Par exemple, si je fais ceci :

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

Alors j'appelle ça :

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

Ensuite, que se passe-t-il réellement dans le shell JavaScript, les valeurs sont "interpolées" et "avant" que l'instruction ne soit envoyée au serveur, comme ceci :

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

Pour mieux démontrer cela, stockez une fonction "uniquement" sur le serveur :

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

Essayez ensuite d'exécuter l'instruction d'agrégation :

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

Et cela entraînera une exception :

E QUERY [main] ReferenceError :bonjour n'est pas défini à (shell):1:69

En effet, l'exécution se produit sur le "client" et non sur le "serveur" et la fonction n'existe pas sur le client.

Le cadre d'agrégation ne peut pas exécuter JavaScript, car il n'a aucune disposition pour le faire. Toutes les opérations sont effectuées en code natif, sans qu'aucun moteur JavaScript ne soit appelé. Par conséquent, vous utilisez plutôt les opérateurs :

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

Si vous ne pouvez pas utiliser les opérateurs pour obtenir les résultats, la seule façon d'exécuter du code JavaScript est d'exécuter mapReduce à la place, qui utilise bien sûr un moteur JavaScript pour s'interfacer avec les données de la collection. Et à partir de là, vous pouvez également référencer une fonction côté serveur dans votre logique si vous en avez besoin :

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

Renvoie :

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

Il ne s'agit donc pas de "comment transmettre une valeur de champ", mais plutôt du fait que le framework d'agrégation ne prend en charge en aucune façon JavaScript, et que ce que vous pensiez qu'il se passait n'est en fait pas le cas.