web-dev-qa-db-ja.com

複雑なオブジェクトをredisに保存する方法(redis-pyを使用)

Hmset関数は各フィールドの値を設定できますが、値自体が複雑な構造化オブジェクトである場合、hgetから返される値は元のオブジェクトではなく、シリアル化された文字列であることがわかりました

例えば

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)

iの型は文字列であり、pythonオブジェクトではありません。各フィールドを手動で解析する以外にこの問題を解決する方法はありますか?

51
yuan

Redisではネストされた構造を作成できません。つまり、ネイティブredisハッシュマップ内にネイティブredisリストを保存することはできません。

ネストされた構造が本当に必要な場合は、代わりにJSON-blob(または同様のもの)を保存することをお勧めします。別のオプションは、マップキーの値として別のredisオブジェクトに「id」/キーを保存することですが、これには完全なオブジェクトを取得するためにサーバーへの複数の呼び出しが必要です。

46
Jonatan Hedborg

実際、組み込みモジュール pickle を使用して、pythonオブジェクトをredisに保存できます。

以下に例を示します。

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

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

ライブラリ、 SubRedis を作成しました。これにより、redisでより複雑な構造/階層を作成できます。 redisインスタンスとプレフィックスを指定すると、ほぼ完全に機能する独立したredisインスタンスが提供されます。

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

SubRedisは、渡された文字列をプレフィックスとしてフラットredisデータ構造に追加するだけです。これは、redisで多くのことを行うパターンの便利なラッパーであることがわかりました。つまり、データをネストするためにidを追加します。

6
Doug T.

以下は、データ構造をピクルス/ピクルス解除するRedisの簡単なラッパーです。

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')]

Redisのプレーンテキストで読み取り可能なデータを好む場合(pickleはそのバイナリバージョンを格納します)、pickle.dumpsをreprに、pickle.loadsをast.literal_evalに置き換えることができます。 jsonの場合、json.dumpsおよびjson.loadsを使用します。

常に単純な文字列であるキーを使用する場合は、キーから酸洗いを削除できます。

3
Curtis Yallop

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

pythonタイプをRedisタイプに、またはその逆に変換します。

>>> 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'>

免責事項:ライブラリを作成しました。コードは次のとおりです。 https://github.com/seperman/redisworks

3
Seperman