web-dev-qa-db-fra.com

comment stocker un objet complexe dans redis (en utilisant redis-py)

La fonction hmset peut définir la valeur de chaque champ, mais j'ai constaté que si la valeur elle-même est un objet structuré complexe, la valeur renvoyée par hget est une chaîne sérialisée, pas l'objet d'origine.

par exemple

images= [{'type':'big', 'url':'....'},
     {'type':'big', 'url':'....'},
     {'type':'big', 'url':'....'}]   

redis = Redis()
redis.hset('photo:1', 'images', images)

i = redis.hget('photo:1', 'images')
print type(i)

le type de i est une chaîne, pas un objet python, existe-t-il un moyen de résoudre ce problème en plus d'analyser manuellement chaque champ?

51
yuan

Vous ne pouvez pas créer de structures imbriquées dans Redis, ce qui signifie que vous ne pouvez pas (par exemple) stocker une liste redis native dans une carte de hachage redis native.

Si vous avez vraiment besoin de structures imbriquées, vous voudrez peut-être simplement stocker un blob JSON (ou quelque chose de similaire). Une autre option consiste à stocker un "id"/clé sur un autre objet Redis en tant que valeur de la clé de carte, mais cela nécessite plusieurs appels au serveur pour obtenir l'objet complet.

46
Jonatan Hedborg

En fait, vous pouvez stocker python des objets dans Redis en utilisant le module intégré pickle .

Voici un exemple.

import pickle
import redis

r = redis.StrictRedis(Host='localhost', port=6379, db=0)
obj = ExampleObject()
pickled_object = pickle.dumps(obj)
r.set('some_key', pickled_object)
unpacked_object = pickle.loads(r.get('some_key'))
obj == unpacked_object
79

Exemple JSON:

import json
import redis

r = redis.StrictRedis(Host='localhost', port=6379, db=0)

images= [
    {'type':'big', 'url':'....'},
    {'type':'big', 'url':'....'},
    {'type':'big', 'url':'....'},
]

json_images = json.dumps(images)
r.set('images', json_images)
unpacked_images = json.loads(r.get('images'))
images == unpacked_images

python 3:

unpacked_images = json.loads(r.get('images').decode('utf-8'))
images == unpacked_images
41
CivFan

J'ai créé une bibliothèque, SubRedis , qui vous permet de créer des structures/hiérarchies beaucoup plus complexes dans Redis. Si vous lui donnez une instance redis et un préfixe, vous obtenez une instance redis presque totalement capable et indépendante.

redis = Redis()
photoRedis = SubRedis("photo:%s" % photoId, redis)
photoRedis.hmset('image0', images[0])
photoRedis.hmset('image1', images[1])
...

SubRedis finit juste par ajouter la chaîne passée en préfixe sur la structure de données plate redis. Je trouve cela très pratique pour un motif que je finis souvent par redis - en ajoutant un identifiant à imbriquer certaines données.

6
Doug T.

Voici un simple wrapper autour de Redis qui décape/décompresse les structures de données:

from redis import Redis
from collections import MutableMapping
from pickle import loads, dumps


class RedisStore(MutableMapping):

    def __init__(self, engine):
        self._store = Redis.from_url(engine)

    def __getitem__(self, key):
        return loads(self._store[dumps(key)])

    def __setitem__(self, key, value):
        self._store[dumps(key)] = dumps(value)

    def __delitem__(self, key):
        del self._store[dumps(key)]

    def __iter__(self):
        return iter(self.keys())

    def __len__(self):
        return len(self._store.keys())

    def keys(self):
        return [loads(x) for x in self._store.keys()]

    def clear(self):
        self._store.flushdb()


d = RedisStore('redis://localhost:6379/0')
d['a'] = {'b': 1, 'c': 10}
print repr(d.items())
# this will not work: (it updates a temporary copy and not the real data)
d['a']['b'] = 2
print repr(d.items())
# this is how to update sub-structures:
t = d['a']
t['b'] = 2
d['a'] = t
print repr(d.items())
del d['a']

# Here is another way to implement dict-of-dict eg d['a']['b']
d[('a', 'b')] = 1
d[('a', 'b')] = 2
print repr(d.items())
# Hopefully you do not need the equivalent of d['a']
print repr([{x[0][1]: x[1]} for x in d.items() if x[0][0] == 'a'])
del d[('a', 'b')]
del d[('a', 'c')]

Si vous préférez des données lisibles en texte clair dans redis (pickle en stocke une version binaire), vous pouvez remplacer pickle.dumps avec repr et pickle.loads avec ast.literal_eval. Pour json, utilisez json.dumps et json.loads.

Si vous utilisez toujours des clés qui sont une simple chaîne, vous pouvez supprimer le pickling de la clé.

3
Curtis Yallop

Vous pouvez utiliser la bibliothèque RedisWorks.

pip install redisworks

>>> from redisworks import Root
>>> root = Root()
>>> root.something = {1:"a", "b": {2: 2}}  # saves it as Hash
>>> print(root.something)  # loads it from Redis
{'b': {2: 2}, 1: 'a'}
>>> root.something['b'][2]
2

Il convertit les types python en types Redis et inversement.

>>> root.sides = [10, [1, 2]]  # saves it as list in Redis.
>>> print(root.sides)  # loads it from Redis
[10, [1, 2]]
>>> type(root.sides[1])
<class 'list'>

Disclaimer: J'ai écrit la bibliothèque. Voici le code: https://github.com/seperman/redisworks

3
Seperman