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

classificar por valor de objeto incorporado no Mongodb


Se você precisar calcular algo assim em tempo de execução, com conteúdo "filtrado" do array determinando a ordem de classificação, é melhor fazer algo com .aggregate() para remodelar e determinar um valor de classificação como este:
db.collection.aggregate([
    // Pre-filter the array elements
    { "$project": {
        "tags": 1,
        "score": {
            "$setDifference": [
                { "$map": {
                    "input": "$tags",
                    "as": "tag",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el.id", "t1" ] },
                            "$$el.score",
                            false
                        ]
                    }
                }},
                [false]
            ]
        }
    }},
    // Unwind to denormalize
    { "$unwind": "$score" },
    // Group back the "max" score
    { "$group": {
        "_id": "$_id",
        "tags": { "$first": "$tags" },
        "score": { "$max": "$score" }
    }},
    // Sort descending by score
    { "$sort": { "score": -1 } }
])

Onde a primeira parte do pipeline é usada para "pré-filtrar" o conteúdo do array (além de manter o campo original) apenas para aqueles valores de "score" onde o id é igual a "t1". Isso é feito processando $map que aplica uma condição a cada elemento via $cond para determinar se deve retornar a "pontuação" para esse elemento ou false .

O $setDifference operação faz uma comparação com uma matriz de elemento único [false] que efetivamente remove qualquer false valores retornados do $map . Como um "conjunto", isso também remove entradas duplicadas, mas para fins de classificação aqui, isso é uma coisa boa.

Com a matriz reduzida e remodelada para valores, você processa $unwind pronto para o próximo estágio para lidar com os valores como elementos individuais. O $group estágio se aplica essencialmente $max na "pontuação" para retornar o valor mais alto contido nos resultados filtrados.

Depois é só aplicar o $sort sobre o valor determinado para ordenar os documentos. Naturalmente, se você quiser isso ao contrário, use $min e classifique em ordem crescente.

Claro, adicione um $match stage para o início se tudo o que você realmente quer são documentos que realmente contenham valores "t1" para id dentro das etiquetas. Mas essa parte é de menor relevância para a classificação dos resultados filtrados que você deseja alcançar.

A alternativa ao cálculo é fazer tudo enquanto você escreve entradas na matriz nos documentos. Meio confuso, mas é mais ou menos assim:
db.collection.update(
    { "_id": docId },
    {
        "$push": { "tags": { "id": "t1", "score": 60 } },
        "$max": { "maxt1score": 60 },
        "$min": { "mint1score": 60 }
    }
)

Aqui está o $max O operador update somente define o valor para o campo especificado se o novo valor for maior que o valor existente ou se ainda não existir nenhuma propriedade. O caso inverso é verdadeiro para $min , onde somente se menor que será substituído pelo novo valor.

É claro que isso teria o efeito de adicionar várias propriedades adicionais aos documentos, mas o resultado final é que a classificação é bastante simplificada:
db.collection.find().sort({ "maxt1score": -1 })

E será muito mais rápido do que calcular com um pipeline de agregação.

Portanto, considere os princípios de design. Dados estruturados em matrizes em que você deseja resultados filtrados e emparelhados para classificação significa calcular em tempo de execução para determinar em qual valor classificar. Adicionando propriedades adicionais ao documento em .update() significa que você pode simplesmente fazer referência a essas propriedades para classificar diretamente os resultados.