Get document's placement in collection based o

2019-07-16 03:36发布

I'm new to MongoDB (+Mongoose). I have a collection of highscores with documents that looks like this:

{id: 123, user: 'User14', score: 101}
{id: 231, user: 'User10', score: 400}
{id: 412, user: 'User90', score: 244}
{id: 111, user: 'User12', score: 310}
{id: 221, user: 'User88', score: 900}
{id: 521, user: 'User13', score: 103}

+ thousands more...

now I'm getting the top 5 players like so:

highscores
    .find()
    .sort({'score': -1})
    .limit(5)
    .exec(function(err, users) { ...code... });

which is great, but I would also like to make a query like "What placement does user12 have on the highscore list?"

Is that possible to achieve with a query somehow?

2条回答
你好瞎i
2楼-- · 2019-07-16 04:01

If you don't have to get the placement in real time, Neil Lunn's answer is perfect. But if your app has always data to insert in this collection, for the new data, you can't get the placement for it.

Here is another solution:

Firstly, you add index on the field score in this collection. Then use query db.highscores.count({score:{$gt: user's score}). It will count the documents whose score it greater than the target. This number is the placement.

查看更多
Evening l夕情丶
3楼-- · 2019-07-16 04:09

It is possible to do this with mapReduce, but it does require that you have an index on the sorted field, so first, if you already have not done:

db.highscores.ensureIndex({ "score": -1 })

Then you can do this:

db.highscores.mapReduce(
    function() {
        emit( null, this.user );
    },
    function(key,values) {
        return values.indexOf("User12") + 1;
    },
    {
        "sort": { "score": -1 },
        "out": { "inline": 1 }
    }
)

Or vary that to the information you need to return other than simply the "ranking" position. But since that is basically putting everything into a large array that has already been sorted by score then it probably will not be the best performance for any reasonable size of data.

A better solution would be to maintain a separate "rankings" collection, which you can again update periodically with mapReduce, even though it does not do any reducing:

db.highscores.mapReduce(
    function() {
        ranking++;
        emit( ranking, this );
    },
    function() {},
    {
        "sort": { "score": -1 },
        "scope": { "ranking": 0 },
        "out": {
            "replace": "rankings"
        }
    }
)

Then you can query this collection in order to get your results:

db.rankings.find({ "value.user": "User12 })

So that would contain the ranking as "emitted" in the _id field of the "rankings" collection.

查看更多
登录 后发表回答