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

Agrupando documentos no MongoDB em condição especial

Isenção de responsabilidade


Antes de ler o restante da resposta, leia https://docs. mongodb.com/manual/core/aggregation-pipeline-limits/ Espera-se que o documento resultante na pergunta tenha uma matriz de todos os documentos que pertencem a uma determinada faixa etária.O tamanho dessa matriz não pode exceder 16 MB , portanto, o código abaixo funcionará apenas para coleções muito pequenas de documentos minúsculos.

O código:
db.collection.aggregate([
    { $sort: { age: 1 } },
    { $group: {
            _id: null,
            ages: { $push: "$age" }
    } },
    { $addFields: {
        ranges: { $reduce: { 
            input: { $range: [ 1, { $size: "$ages" }, 1 ] }, 
            initialValue: [ [ { $arrayElemAt: [ "$ages", 0 ] } ] ], 
            in: { $cond: { 
                if:  { $gt: [
                    { $subtract: [ { $arrayElemAt: [ "$ages", "$$this" ] }, { $arrayElemAt: [ "$ages", { $subtract: [ "$$this", 1 ] } ] } ] },
                    2
                    ] }, 
                then: { $concatArrays: [ "$$value",  [ [ { $arrayElemAt: [ "$ages", "$$this" ] } ] ] ] }, 
                else: { $concatArrays: [ 
                    { $slice: [ "$$value" , { $subtract: [ { $size: "$$value" }, 1 ] } ] },
                    [ { $concatArrays: [ 
                        { $arrayElemAt: [ { $slice: [ "$$value" , -1 ] }, 0 ] }  ,  
                        [ { $arrayElemAt: [ "$ages", "$$this" ] } ]
                    ]  } ]
                ] }
            } }
        } } 
    } },
    { $unwind: "$ranges" }, 
    { $lookup: {
       from: "collection",
       localField: "ranges",
       foreignField: "age",
       as: "group"
     } },
     { $project: { _id: 0, group: 1 } }
])

A parte que pode exigir um pouco de explicação é como calcular as faixas etárias.

Para isso, pegamos todas as idades usando $group em uma única matriz e, em seguida, $addFields "intervalos" - uma matriz 2D de faixas etárias com lacunas entre a pessoa mais velha em um grupo mais jovem e uma pessoa mais jovem no grupo mais velho é maior que 2 anos.

A matriz é calculada usando $reduce de um $range matriz de índices de todas as idades, mas primeiro, que vai para o valor inicial.

A expressão de redução é um $cond que calcula a diferença entre o atual e o anterior ($subtract ) elemento da matriz de todas as idades.

Se for maior que 2, uma nova faixa etária será adicionada usando $concatArrays . Caso contrário, a idade é adicionada ao grupo mais antigo usando $slice para enviar para o último grupo na matriz de intervalos e $setUnion para eliminar duplicatas.

Quando as faixas etárias são calculadas, $lookup a mesma coleção por idade para agrupá-los na matriz "grupo".