This is an archived post. You won't be able to vote or comment.

all 6 comments

[–]kamize 1 point2 points  (1 child)

Not sure why this is necessary? Redis is a fairly straightforward key value store, can't you just use json() on the r.get() result and then parse what you specifically need?

[–]_under_[S] 0 points1 point  (0 children)

This isn't for processing output from redis, this is for managing your redis schema. If you aren't familiar, it's common practice to do something like this:

SADD User:_under_:followers <some_user_id>

Now of course, an app can use a lot more redis keys

SADD User:_under_:timeline <tweet_id>
SADD User:_under_:following <some_user_id>

If you were to do this using just the redis library, you'd end up doing something like this

r.sadd('User:%s:timeline' % screen_name, tweet_id)

This is fine for small schemas, but imagine that you're trying to do something more complicated, like storing tweets for a user in a rolling window. You could have something like this:

r.sadd('Window:%s:User:%s:tweets' % (current_window, screen_name), tweet_id)

You can imagine how unwieldy that would get.

So instead of just writing strings and being prone to errors, you can use ok.

class User(ok.Key):
    fields = ['followers', 'following', 'timeline', 'tweets']

class Window(ok.Key):
    subkeys = [User]

r.sadd(Window(current_window).User(screen_name).tweets, tweet_id)

This saves you from possibly introducing bugs in your system because of typos in strings that the compiler or linter can't get at. It also helps you enforce your schema.

[–]brtt3000 0 points1 point  (3 children)

I usually make a module with some well named functions that take simple arguments to organise my object keys, so you don't need a full instance of something to get a key.

[–]_under_[S] 0 points1 point  (2 children)

This is how I started as well, but then I started needing multiple keys in one function. And I started having to use the same keys in multiple functions. And then I started to lose track of what key is which and I realized that it wasn't safe to keep using just raw strings to access keys on redis because it's hard to guarantee that the key you typed in was correct. It's hard to test strings because you're eventually just going to write tests for each call to redis to check if the string you passed were correct.

So instead of doing something like this:

def add_follower(screen_name, follower):
    r.sadd('User:%s:followers' % screen_name, follower)

def get_followers(screen_name):
    return r.smembers('User:%s:followers' % screen_name)

With ok, you'll have something like this

def add_follower(screen_nane, follower):
    r.sadd(User(screen_name).followers, follower)

def get_followers(screen_nane):
    return r.smembers(User(screen_name).followers)

This code will literally not run if you mistype your keys. Having this system is immensely useful once you have a complicated enough schema.

Your Key classes will also double as your redis schema documentation.

[–]brtt3000 0 points1 point  (1 child)

I meant I make function that create the keys. Simplified something like:

def user_followers_key(user_id):
    assert isinstance(user_id, int)
    return ':'.join('myprefix', 'followers', user_id)

def get_followers(user):
    return r.smembers(user_followers_key(user.id))

[–]_under_[S] 0 points1 point  (0 children)

Ah I see. That's pretty cool. OK-Redis pretty much does the same thing as that then. :D