MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Colunas calculadas de funções personalizadas projeção mongodb


Você parece pensar que é possível chamar uma função JavaScript no pipeline de agregação, mas não pode fazer isso. Você está confundindo o que é realmente "interpolação" de uma variável de um resultado de função para execução dentro do pipeline.

Por exemplo, se eu fizer isso:
var getNumbers = function() { return [ 1,2,3 ] };

Então eu chamo isso:
db.collection.aggregate([
    { "$project": {
        "mynums": getNumbers()
    }}  
])

Então o que realmente acontece no shell do JavaScript os valores estão sendo "interpolados" e "antes" da instrução ser enviada ao servidor, assim:
db.collection.aggregate([
    { "$project": {
        "mynums": [1,2,3]
    }}  
])

Para demonstrar ainda mais isso, armazene uma função "somente" no servidor:
db.system.js.save({ "_id": "hello", "value": function() { return "hello" } })

Em seguida, tente executar a instrução de agregação:
db.collection.aggregate([
    { "$project": {
        "greeting": hello()
    }}  
])

E isso resultará em uma exceção:

E QUERY [main] ReferenceError:hello não está definido em (shell):1:69

O que ocorre porque a execução está acontecendo no "cliente" e não no "servidor" e a função não existe no cliente.

A estrutura de agregação não pode execute JavaScript, pois não tem provisão para isso. Todas as operações são executadas em código nativo, sem que nenhum mecanismo JavaScript seja invocado. Portanto, você usa os operadores lá:
db.collection.aggregate([
    { "$project": {
        "total": { "$add": [ 1, 2 ] },
        "field_total": { "$subtract": [ "$gross", "$tax" ] }
    }}  
])   

Se você não puder usar os operadores para obter os resultados, a única maneira de executar o código JavaScript é executar mapReduce, que obviamente usa um mecanismo JavaScript para interagir com os dados da coleção. E a partir daí você também pode referenciar uma função do lado do servidor dentro de sua lógica se precisar:
{ "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 } }
)

Devoluções:
{
    "_id": 1,
    "value": 14
}

Portanto, não se trata de "como passar um valor de campo", mas sim do fato de que a estrutura de agregação não suporta JavaScript de forma alguma e que o que você pensou que estava acontecendo não é realmente o caso.