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

limite e classifique cada grupo no mongoDB usando agregação


Sua melhor opção aqui é executar consultas separadas para cada "País" (de preferência em paralelo) e retornar os resultados combinados. As consultas são bastante simples, e apenas retornam os 2 principais valores após aplicar uma classificação no valor de classificação e serão executadas rapidamente, mesmo que você precise realizar várias consultas para obter o resultado completo.

A estrutura de agregação não é adequada para isso, agora e mesmo em um futuro próximo. O problema é que não existe um operador que "limite" o resultado de qualquer agrupamento de alguma forma. Então, para fazer isso, você basicamente precisa $push todo o conteúdo em uma matriz e extraia os valores "n superiores" disso.

As operações atuais necessárias para fazer isso são bastante horríveis, e o problema principal é que os resultados provavelmente excederão o limite BSON de 16 MB por documento na maioria das fontes de dados reais.

Também há um n complexidade para isso devido a como você teria que fazer isso agora. Mas só para demonstrar com 2 itens:
db.collection.aggregate([
    // Sort content by country and rating
    { "$sort": { "Country": 1, "rating": -1 } },

    // Group by country and push all items, keeping first result
    { "$group": {
        "_id": "$Country",
        "results": {
            "$push": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        },
        "first": { 
            "$first": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        }
    }},

    // Unwind the array
    { "$unwind": "results" },

    // Remove the seen result from the array
    { "$redact": {
        "$cond": {
            "if": { "$eq": [ "$results.id", "$first.id" ] },
            "then": "$$PRUNE",
            "else": "$$KEEP"
        }
    }},

    // Group to return the second result which is now first on stack
    { "$group": {
        "_id": "$_id",
        "first": { "$first": "$first" },
        "second": { 
            "$first": {
                "name": "$results.name", 
                "rating": "$results.rating",
                "id": "$results.id"
            }
        }
    }},

    // Optionally put these in an array format
    { "$project": {
        "results": { 
            "$map": {
                "input": ["A","B"],
                "as": "el",
                "in": {
                    "$cond": {
                        "if": { "$eq": [ "$$el", "A" ] },
                        "then": "$first",
                        "else": "$second"
                    }
                }
            }
        }
    }}
])

Isso obtém o resultado, mas não é uma ótima abordagem e fica muito mais complexo com iterações para limites mais altos ou mesmo onde os agrupamentos têm possivelmente menos que n resultados para retornar em alguns casos.

A série de desenvolvimento atual ( 3.1.x ) no momento da escrita tem um $slice operador que torna isso um pouco mais simples, mas ainda tem a mesma armadilha de "tamanho":
db.collection.aggregate([
    // Sort content by country and rating
    { "$sort": { "Country": 1, "rating": -1 } },

    // Group by country and push all items, keeping first result
    { "$group": {
        "_id": "$Country",
        "results": {
            "$push": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        }
    }},
    { "$project": {
        "results": { "$slice": [ "$results", 2 ] }
    }}
])

Mas basicamente até que o framework de agregação tenha alguma forma de "limitar" o número de itens produzidos pelo $push ou um operador de "limite" de agrupamento semelhante, a estrutura de agregação não é realmente a solução ideal para esse tipo de problema.

Consultas simples como esta:
db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)

Executar para cada país distinto e, idealmente, em processamento paralelo por loop de evento de encadeamento com um resultado combinado produz a abordagem mais ideal no momento. Eles apenas buscam o que é necessário, que é o grande problema que a estrutura de agregação ainda não consegue lidar em tal agrupamento.

Portanto, procure suporte para fazer esses "resultados de consulta combinados" da maneira mais ideal para o idioma escolhido, pois será muito menos complexo e com muito mais desempenho do que jogar isso na estrutura de agregação.