MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Comment obtenir le classement des éléments dans une liste triée par plusieurs champs dans Mongoose

Comptez le nombre d'utilisateurs qui précèdent cet utilisateur dans votre ordre de tri. Je vais commencer par le cas d'un tri simple (tri non composé) car la requête dans le cas composé est plus compliquée, même si l'idée est exactement la même.

> db.test.drop()
> for (var i = 0; i < 10; i++) db.test.insert({ "x" : i })
> db.test.find({ }, { "_id" : 0 }).sort({ "x" : -1 }).limit(5)
{ "x" : 9 }
{ "x" : 8 }
{ "x" : 7 }
{ "x" : 6 }
{ "x" : 5 }

Pour cette commande, le classement d'un document { "x" : i } est le nombre de documents { "x" : j } avec i < j

> var rank = function(id) {
    var i = db.test.findOne({ "_id" : id }).x
    return db.test.count({ "x" : { "$gt" : i } })
}
> var id = db.test.findOne({ "x" : 5 }).id
> rank(id)
4

Le classement sera basé sur 0. De même, si vous souhaitez calculer le classement du document { "x" : i } dans le tri { "x" : 1 } , vous compteriez le nombre de docs { "x" : j } avec i > j .

Pour un tri composé, la même procédure fonctionne, mais elle est plus délicate à mettre en œuvre car l'ordre dans un index composé est lexicographique, c'est-à-dire pour le tri { "a" : 1, "b" : 1} , (a, b) < (c, d) si a < c ou a = c et b < d , nous avons donc besoin d'une requête plus compliquée pour exprimer cette condition. Voici un exemple d'index composé :

> db.test.drop()
> for (var i = 0; i < 3; i++) {
    for (var j = 0; j < 3; j++) {
        db.test.insert({ "x" : i, "y" : j })
    }
}
> db.test.find({}, { "_id" : 0 }).sort({ "x" : 1, "y" : -1 })
{ "x" : 0, "y" : 2 }
{ "x" : 0, "y" : 1 }
{ "x" : 0, "y" : 0 }
{ "x" : 1, "y" : 2 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 0 }
{ "x" : 2, "y" : 2 }
{ "x" : 2, "y" : 1 }
{ "x" : 2, "y" : 0 }

Pour trouver le rang du document { "x" : i, "y" : j } , vous devez trouver le nombre de documents { "x" : a, "y" : b } dans l'ordre { "x" : 1, "y" : -1 } tel que (i, j) < (a, b) . Compte tenu de la spécification de tri, cela équivaut à la condition i < a ou i = a et j > b :

> var rank = function(id) {
    var doc = db.test.findOne(id)
    var i = doc.x
    var j = doc.y
    return db.test.count({
        "$or" : [
            { "x" : { "$lt" : i } },
            { "x" : i, "y" : { "$gt" : j } }
        ]
    })
}
> id = db.test.findOne({ "x" : 1, "y" : 1 })._id
> rank(id)
4

Enfin, dans votre cas d'un index composé en trois parties

{ "score" : -1, "time" : 1, "bonus" : -1 }

le rank fonction serait

> var rank = function(id) {
    var doc = db.test.findOne(id)
    var score = doc.score
    var time = doc.time
    var bonus = doc.bonus
    return db.test.count({
        "$or" : [
            { "score" : { "$gt" : score } },
            { "score" : score, "time" : { "$lt" : time } },
            { "score" : score, "time" : time, "bonus" : { "$gt" : bonus } }
        ]
    })
}