# How to model a Leaderboard

**URL:** <https://forums.foundationdb.org/t/how-to-model-a-leaderboard/373>\
**Category:** Using FoundationDB\
**Created:** [May 8, 2018, 5:22am UTC](https://forums.foundationdb.org/t/how-to-model-a-leaderboard/373 "2018-05-08T05:22:07Z")\
**Posts on this page:** 1\
**Showing post:** 3

<div class="post-metadata">

**Author:** ![amirouche](https://sea1.discourse-cdn.com/foundationdb/user_avatar/forums.foundationdb.org/amirouche/32/2096_2.png) [@amirouche](https://forums.foundationdb.org/u/amirouche)\
**Post date:** [May 8, 2018, 2:22pm UTC](https://forums.foundationdb.org/t/how-to-model-a-leaderboard/373/3 "2018-05-08T14:22:50Z")

</div>

Here is something to get you started in Python:

```python
from operator import itemgetter

import fdb
from fdb.tuple import pack
from fdb.tuple import unpack

fdb.api_version(510)

@fdb.transactional
def get_score(tr, user):
    """Return the score of the user.

    Add the user to the leaderboard if they are not already there

    """
    user_key = pack(('scores', user))
    score = tr.get(user_key)
    if score == None:
        # init the score at zero
        score = 0
        tr.set(user_key, pack((score,)))
        # Also store the user in the leaderboard.
        # Here the value is empty and the full data is encoded
        # in the key in order to make each key unique and keep
        # the leaderboard sorted.
        tr.set(pack(('leaderboard', score, user)), b'')
    else:
        score = unpack(score)[0]
    return score

@fdb.transactional
def add(tr, user, increment=1):
    """Add INCREMENT to USER score.

    Return the new score of the user"""
    score = get_score(tr, user)
    total = score + increment
    # update the user's score...
    user_key = pack(('scores', user))
    tr.set(user_key, pack((total,)))
    # ...and the leaderboard
    tr.clear(pack(('leaderboard', score, user)))
    tr.set(pack(('leaderboard', total, user)), b'')
    return total

@fdb.transactional
def top(tr, count=3):
    out = dict()
    # iterate over the whole 'leaderboard' namespace in reverse order
    iterator = tr.get_range_startswith(pack(('leaderboard',)), reverse=True)
    for key, _ in iterator:
        _, score, user = unpack(key)
        if score in out.keys():
            out[score].append(user)
        elif len(out.keys()) == count:
            break
        else:
            out[score] = [user]

    # output a sorted dict (python >= 3.6)
    return dict(sorted(out.items(), key=itemgetter(0), reverse=True))

```

Here is an example REPL run:

```auto
In [1]: import fdb

In [2]: import leaderboard

In [3]: db = fdb.open()

In [4]: 

In [4]: leaderboard.add(db, 'amirouche', 42)
Out[4]: 42

In [5]: leaderboard.top(db)
Out[5]: {0: ['nitu'], 42: ['amirouche']}

In [6]: leaderboard.add(db, 'nitu', 42)
Out[6]: 42

In [7]: leaderboard.top(db)
Out[7]: {42: ['nitu', 'amirouche']}

In [8]: leaderboard.add(db, 'amirouche', 42)
Out[8]: 84

In [9]: leaderboard.top(db)
Out[9]: {42: ['nitu'], 84: ['amirouche']}

```

Feel free to ask question, there is no stupid question, especially at this point.

Also, I’ve been answering question about how to create applications ontop key-value stores on stackoverflow, see “[Find by value on leveldb](https://stackoverflow.com/questions/48285501/find-by-value-on-leveldb/48299940#48299940)” and “[Key Value Store in NoSQL Database Systems](https://stackoverflow.com/questions/47998234/key-value-store-in-nosql-database-systems/48299676#48299676)”.

BTW, before some ask the question, I don’t think it’s worth learning wiredtiger or leveldb or lmdb at this point (except maybe if you plan to do [event sourcing like SkuVault](https://forums.foundationdb.org/t/success-story-foundationdb-at-skuvault/336/6?u=amirouche)).

edit: copy/paste failure

---

_[View the full topic](https://forums.foundationdb.org/t/how-to-model-a-leaderboard/373)._
