def redis_test(): rj = Client(host='localhost', port=6379) # Set the key `obj` to some object obj = { 'answer': 42, 'arr': [None, True, 3.14], 'truth': { 'coord': 'out there' } } rj.jsonset('obj', Path.rootPath(), obj) # Get something print ('Is there anybody... {}?'.format( rj.jsonget('obj', Path('.truth.coord')) )) # Delete something (or perhaps nothing), append something and pop it rj.jsondel('obj', Path('.arr[0]')) rj.jsonarrappend('obj', Path('.arr'), 'something') print ('{} popped!'.format(rj.jsonarrpop('obj', Path('.arr')))) # Update something else rj.jsonset('obj', Path('.answer'), 2.17) # And use just like the regular redis-py client jp = rj.pipeline() jp.set('foo', 'bar') jp.jsonset('baz', Path.rootPath(), 'qaz') jp.execute()
def testUsageExampleShouldSucceed(self): "Test the usage example" # Create a new rejson-py client rj = Client(host='localhost', port=port, decode_responses=True) # Set the key `obj` to some object obj = { 'answer': 42, 'arr': [None, True, 3.14], 'truth': { 'coord': 'out there' } } rj.jsonset('obj', Path.rootPath(), obj) # Get something rv = rj.jsonget('obj', Path('.truth.coord')) self.assertEqual(obj['truth']['coord'], rv) # Delete something (or perhaps nothing), append something and pop it value = "something" rj.jsondel('obj', Path('.arr[0]')) rj.jsonarrappend('obj', Path('.arr'), value) rv = rj.jsonarrpop('obj', Path('.arr')) self.assertEqual(value, rv) # Update something else value = 2.17 rj.jsonset('obj', Path('.answer'), value) rv = rj.jsonget('obj', Path('.answer')) self.assertEqual(value, rv) # And use just like the regular redis-py client jp = rj.pipeline() jp.set('foo', 'bar') jp.jsonset('baz', Path.rootPath(), 'qaz') jp.execute() rv1 = rj.get('foo') self.assertEqual('bar', rv1) rv2 = rj.jsonget('baz') self.assertEqual('qaz', rv2)
class ReJson: """Facade for ReJson""" def __init__(self, host: str, port: Union[str, int]) -> None: """Instantiate a connection to ReJson. :param host: The hostname/ip of the Redis instance. :type host: str :param port: The port of the Redis instance. :type port: int """ self._client = Client(host=host, port=port, decode_responses=True) def keys(self) -> Json: """Get all keys""" return self._client.keys() def post(self, key: str, obj: Json) -> None: """Post a new Json object to the store. :param key: The key to store the Json at. :type key: str :param obj: What to store. :type obj: Json """ self._client.jsonset(key, Path.rootPath(), obj) def get(self, key: str) -> Json: """[summary] :param key: The key that the Json object was stored at. :type key: str :return: The Json stored at `key`. :rtype: Json """ return self._client.jsonget(key, Path.rootPath()) def update(self, key: str, path: str, value: Json) -> None: """[summary] :param key: The key that the Json object was stored at. :type key: str :param path: A period seperated string of keys to traverse the Json. :type path: str :param value: The new value. :type value: Json """ self._client.jsonset(key, Path(f".{path}"), value) def append(self, key: str, path: str, *values: Json) -> None: """Append to some array within a Json obejct. :param key: The key that the Json object was stored at. :type key: str :param path: A period seperated string of keys to traverse the Json. :type path: str """ self._client.jsonarrappend(key, Path(f".{path}"), *values) def pop(self, key: str, path: str) -> Json: """Pop from from array within a Json object. :param key: The key that the Json object was stored at. :type key: str :param path: A period seperated string of keys to traverse the Json. :type path: str :return: The Json value popped from the array. :rtype: Json """ return self._client.jsonarrpop(key, f".{path}") def remove(self, key: str, path: str, value: Json) -> None: """Remove something from some array within a Json object. :param key: The key that the Json object was stored at. :type key: str :param path: A period seperated string of keys to travers the Json. :type path: str :param value: The value to remove from the array. :type value: Json """ index = self._client.jsonarrindex(key, f".{path}", value) self._client.jsondel(key, f"{path}[{index}]")
class RedisJson: def __init__(self): self._rjson = Client(host='localhost', port=6379, decode_responses=True) self._root_path = Path.rootPath() ''' Insert JSON into db Structure of JSON to insert: { 'lat' : 80.844, 'long' : -43.139, 'resources' : { 'mask' : 450, 'vaccine' : 56, 'oxygen' : 800, ... }, 'updated' : <unix time ms> } ''' def insert(self, key, data): self._rjson.jsonset(key, self._root_path, data) ''' Return list of all JSON objects stored in db TODO: added this for now, but loading everything in memory doesn't seem like a great idea, maybe RedisSearch will help with this. Or maybe make this return a generator which can be iterated through ''' def get(self): results = [] for key in self._rjson.scan_iter(): results.append(self._rjson.jsonget(key, self._root_path)) return results ''' Update field of a JSON object in db Syntax for `path` argument: E.g. we have { 'key1' : value1, 'key2' : { 'key3' : value2 } } To update value2, `path` should be ".key2.key3" ''' def update(self, key, path, new_value): self._rjson.jsonset(key, path, new_value) ''' Delete a JSON value from the db ''' def delete(self, key): self._rjson.jsondel(key, self._root_path)
'arr': [None, True, 3.14], 'truth': { 'coord': 'out there' } } jsondata = json.dumps(obj) rj.jsonset('obj', Path('A2AA'), obj) # Get something temp = rj.jsonget('obj', Path('A2AA.truth.coord')) print (f'Is there anybody... {temp}?') # Delete something (or perhaps nothing), append something and pop it rj.jsondel('obj', Path('.arr[0]')) rj.jsonarrappend('obj', Path('.arr'), 'something') popped = rj.jsonarrpop('obj', Path('.arr')) print(f'{popped} popped!') # Update something else rj.jsonset('obj', Path('.answer'), 2.17) # And use just like the regular redis-py client jp = rj.pipeline() jp.set('foo', 'bar') jp.jsonset('baz', Path.rootPath(), 'qaz') jp.execute()
class RejsonDb(KeyValueStorage): def __init__(self, conf): """ arguments: conf -- a dictionary containing 'settings' module compatible configuration of the plug-in """ self._host = conf['host'] self._port = int(conf['port']) self._db = int(conf['id']) self.redis = Client(host=self._host, port=self._port, db=self._db, decode_responses=True) self._scan_chunk_size = 50 try: self.redis.jsonget('-') except ResponseError as e: if 'unknown command' in str(e): logging.fatal( "Rejson DB Plug-in requires Redis with RedisJSON module enabled" ) else: raise e def rename(self, key, new_key): return self.redis.rename(key, new_key) def list_get(self, key, from_idx=0, to_idx=-1): """ Returns a stored list. If there is a non-list value stored with the passed key then TypeError is raised. arguments: key -- data access key from_idx -- optional start index to_idx -- optional (default is -1) end index (including, i.e. unlike Python); negative values are supported (-1 = last, -2 = penultimate,...) """ data = self.get(key, []) if isinstance(data, list): if to_idx == -1: return data[from_idx:] return data[from_idx:to_idx + 1] raise TypeError('Object is not a list') def list_append(self, key, value): """ Add a value at the end of a list arguments: key -- data access key value -- value to be pushed """ if not self.exists(key): self.set(key, []) self.redis.jsonarrappend(key, Path.rootPath(), value) def list_pop(self, key): """ Removes and returns the first element of the list stored at key. arguments: key -- list access key """ return self.redis.jsonarrpop(key) def list_len(self, key): """ Returns length of a list. If there is a non-list value stored with the passed key then TypeError is raised. arguments: key -- data access key """ if not self.exists(key): return 0 return self.redis.jsonarrlen(key) def list_set(self, key, idx, value): """ Sets the list element at index to value arguments: key -- list access key idx -- a zero based index where the set should be performed value -- a JSON-serializable value to be inserted """ # TODO the operation pair should be atomic to avoid possible race conditions self.redis.jsonarrpop(key, Path.rootPath(), idx) return self.redis.jsonarrinsert(key, Path.rootPath(), idx, value) def list_trim(self, key, keep_left, keep_right): """ Trims the list from the beginning to keep_left - 1 and from keep_right to the end. The function does not return anything. arguments: key -- data access key keep_left -- the first value to be kept keep_right -- the last value to be kept """ self.redis.jsonarrtrim(key, Path.rootPath(), keep_left, keep_right) def hash_get(self, key, field): """ Gets a value from a hash table stored under the passed key arguments: key -- data access key field -- hash table entry key """ if self.redis.jsontype(key, Path(f'["{field}"]')) is None: return None return self.redis.jsonget(key, Path(f'["{field}"]'), no_escape=True) def hash_set(self, key, field, value): """ Puts a value into a hash table stored under the passed key arguments: key -- data access key field -- hash table entry key value -- a value to be stored """ if not self.exists(key): self.set(key, {}) self.redis.jsonset(key, Path(f'["{field}"]'), value) def hash_del(self, key, field): """ Removes a field from a hash item arguments: key -- hash item access key field -- the field to be deleted """ self.redis.jsondel(key, Path(f'["{field}"]')) def hash_get_all(self, key): """ Returns a complete hash object (= Python dict) stored under the passed key. If the provided key is not present then an empty dict is returned. arguments: key -- data access key """ return self.get(key) def get(self, key, default=None): """ Gets a value stored with passed key and returns its JSON decoded form. arguments: key -- data access key default -- a value to be returned in case there is no such key """ data = self.redis.jsonget(key, Path.rootPath(), no_escape=True) if data is None: return default return data def set(self, key, data): """ Saves 'data' with 'key'. arguments: key -- an access key data -- a dictionary containing data to be saved """ self.redis.jsonset(key, Path.rootPath(), data) def set_ttl(self, key, ttl): """ Set auto expiration timeout in seconds. arguments: key -- data access key ttl -- number of seconds to wait before the value is removed (please note that update actions reset the timer to zero) """ self.redis.expire(key, ttl) def get_ttl(self, key): return self.redis.ttl(key) def clear_ttl(self, key): self.redis.persist(key) def remove(self, key): """ Removes a value specified by a key arguments: key -- key of the data to be removed """ self.redis.jsondel(key) def exists(self, key): """ Tests whether there is a value with the specified key arguments: key -- the key to be tested returns: boolean value """ return self.redis.exists(key) def setnx(self, key, value): """ An atomic operation "set if not exists". returns: 1 if the key was set 0 if the key was not set """ return self.redis.jsonset(key, Path.rootPath(), value, nx=True) def getset(self, key, value): """ An atomic operation which obtains current key first and then sets a new value under that key returns: previous key if any or None """ data = self.get(key) self.set(key, value) return data def incr(self, key, amount=1): """ Increments the value of 'key' by 'amount'. If no key exists, the value will be initialized as 'amount' """ if not self.exists(key): self.set(key, 0) return self.redis.jsonnumincrby(key, Path.rootPath(), amount) def hash_set_map(self, key, mapping): """ Set key to value within hash 'name' for each corresponding key and value from the 'mapping' dict. Before setting, the values are json-serialized """ return self.set(key, mapping)