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.