Angenommen, wir wollen ein System modellieren, bei dem User kurze Informationsschnipsel (Posts) an einen Server schicken können, der diese an alle User verteilt, die vorher angegeben haben, besagten Users zu folgen.
Der Redis-Treiber über Python heißt redis
und wird so importiert und initialisiert. In der globalen Variable r
halte ich eine persistente Datenbank-Verbindung, die ich in allen weiteren Funktionen benutzen werde:
import redis
r = redis.Redis(db=1)
Beginnen wir mit den Usern. Jeder soll einen Namen und Kennwort haben und die Liste der User haben, die ihm folgen (followers) und die, denen er folgt (following). Das ist offensichtlich redundant, aber einfacher zu verwalten. Eine ID identifiziert einen User eindeutig. Diese erzeuge ich mit einem globalen Counter.
class User:
@classmethod
def next_uid(cls):
return r.incr("ids:user")
@classmethod
def create(cls, name, passwd):
uid = cls.next_uid()
r.set("user:%s:name" % uid, name)
r.set("user:%s:passwd" % uid, passwd)
return uid
def __init__(self, uid):
self.uid = uid
self.name = r.get("user:%s:name" % uid)
self.passwd = r.get("user:%s:passwd" % uid)
def __repr__(self):
return "%s [%d]" % (self.name, self.uid)
Die folgenden zwei Methoden implementieren das Verfolgen:
class User:
def follow(self, user):
r.sadd("user:%s:following" % self.uid, user.uid)
r.sadd("user:%s:followers" % user.uid, self.uid)
def unfollow(self, user):
r.srem("user:%s:following" % self.uid, user.uid)
r.srem("user:%s:followers" % user.uid, self.uid)
Nun möchte ich sehen, wer einem User folgt und wem er oder sie folgt:
class User:
def get_followers(self):
return set(User(uid) for uid in r.smembers("uid:%s:followers" % self.uid))
def get_following(self):
return set(User(uid) for uid in r.smembers("uid:%s:following" % self.uid))
Als nächstes implementieren wir die Posts. Sie haben einen Autor, ein Datum und einen Text. Ich speichere sie ebenfalls unter einer ID, die mit einem globalen Counter erzeugt wird. Da ich die Attribute nie einzeln brauche, speichere ich sie kompakt (in der Annahme, ein Name enthält nie ein |
) in nur einem String.
class Post:
@classmethod
def next_uid(cls):
return r.incr("ids:post")
@classmethod
def create(cls, author, date, text):
uid = cls.next_uid()
r.set("post:%s" % uid, "%s|%s|%s" % (author.name, date, text))
return uid
def __init__(self, uid):
self.uid = uid
self.author, self.date, self.text = r.get("post:%s" % uid).split("|", 2)
Damit ich effizient die Liste aller Posts der User anzeigen kann, denen ein User folgt (manchmal auch Freunde genannt), berechne ich sie ein Erzeugen von Posts und nicht erst beim Anzeigen. Jeder User hat eine Timeline der ich neue Posts am Anfang hinzufüge. Außerdem hat jeder User noch ein Archiv mit den eigenen Posts, wo ich ebenfalls neue Posts am Anfang hinzufüge. In beiden Fällen sind die Listen so von neu nach alt sortiert.
class User:
def post(self, text):
post_uid = Post.create(self, datetime.now(), text)
r.lpush("user:%s:archive" % self.uid, post_uid)
for uid in r.smembers("user:%s:followers" % self.uid):
r.lpush("user:%s:timeline" % uid, post_uid)
def get_archive(self, limit=20, offset=0):
return [Post(uid) for uid in r.lrange("user:%s:archive" % self.uid, offset, limit)]
def get_timeline(self, limit=20, offset=0):
return [Post(uid) for uid in r.lrange("user:%s:timeline" % self.uid, offset, limit)]