sql >> Base de Datos >  >> NoSQL >> MongoDB

Funciones personalizadas columnas calculadas mongodb proyección

Parece que piensa que es posible llamar a una función de JavaScript en la canalización de agregación, pero no puede hacerlo. Está confundiendo lo que en realidad es una "interpolación" de una variable del resultado de una función para su ejecución dentro de la canalización.

Por ejemplo, si hago esto:

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

Entonces llamo a esto:

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

Luego, lo que realmente sucede en el shell de JavaScript:los valores se "interpolan" y "antes" de que se envíe la instrucción al servidor, así:

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

Para demostrarlo aún más, almacene una función "solo" en el servidor:

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

Luego intente ejecutar la declaración de agregación:

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

Y eso resultará en una excepción:

E QUERY [main] ReferenceError:hola no está definido en (shell):1:69

Lo cual se debe a que la ejecución está ocurriendo en el "cliente" y no en el "servidor" y la función no existe en el cliente.

El marco de agregación no puede ejecutar JavaScript, ya que no tiene ninguna disposición para hacerlo. Todas las operaciones se realizan en código nativo, sin que se invoque ningún motor de JavaScript. Por lo tanto, usa los operadores allí en su lugar:

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

Si no puede usar los operadores para lograr los resultados, la única forma en que puede ejecutar el código JavaScript es ejecutar mapReduce en su lugar, que por supuesto usa un motor JavaScript para interactuar con los datos de la colección. Y desde allí también puede hacer referencia a una función del lado del servidor dentro de su lógica si necesita:

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

Devoluciones:

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

Por lo tanto, no se trata de "cómo pasar un valor de campo", sino del hecho de que el marco de agregación no es compatible con JavaScript de ninguna manera, y que lo que pensaba que estaba sucediendo no es realmente el caso.