class SSDBClient(object): def __init__(self, host, port, id_litteral=DEFAULT_ID_LITTERAL, count_key=DEFAULT_COUNT_KEY): if not _validate_host(host): raise DatabaseError("Invalid hostname") if not _validate_port(port): raise DatabaseError("Invalid port") if not _validate_string_litteral(id_litteral): raise DatabaseError("Invalid id_litteral") if not _validate_string_litteral(count_key): raise DatabaseError("Invalid count_key") self._cli = Client(host, port) self._id_litteral = id_litteral self._count_key = count_key self.select(DEFAULT_COLLECTION) def _get_coll_count(self): return '{}|count'.format(self.coll) def _get_coll_types(self): return '{}|types'.format(self.coll) def _get_current_id(self, unchange=False): if not self._cli.hexists(self._coll_count, self._count_key): if unchange: return None self._cli.hset(self._coll_count, self._count_key, DEFAULT_ID_START) return DEFAULT_ID_START if unchange: return int(self._cli.hget(self._coll_count, self._count_key)) return int(self._cli.hincr(self._coll_count, self._count_key)) def _get_obj_from_scan(self, scan, this_id): obj = {} for j in xrange(len(scan)/2): this_key = scan[2*j] this_type = _get_type(self._cli.hget(self._coll_types, this_key)) length_unkey = len('{}|{}|'.format(self._id_litteral, this_id)) this_key = this_key[length_unkey:] this_value = this_type(scan[2*j+1]) obj[this_key] = this_value return obj def _check_id(self, id): objs = self.find_all({}, {}, {}, []) id_list = [obj[self._id_litteral] for obj in objs] return id in id_list def _save_document(self, obj, id): for key, value in obj.iteritems(): if isinstance(value, unicode): value = value.encode('utf-8') key_db = '{}|{}|{}'.format(self._id_litteral, id, key) self._cli.hset(self.coll, key_db, str(value)) self._cli.hset(self._coll_types, key_db, str(type(value))) return id def select(self, coll): if not _validate_string_litteral(coll): raise DatabaseError("Invalid collection name") self.coll = coll self._coll_count = self._get_coll_count() self._coll_types = self._get_coll_types() return self.coll def collection(self): return self.coll def insert(self, obj): if not _validate_insert(obj, id_litteral=self._id_litteral): raise DocumentError("Document must be a dictionary and Document must have a specific structure and Document can not contain the '{}' field".format(self._id_litteral)) current_id = self._get_current_id() obj[self._id_litteral] = int(current_id) return self._save_document(obj, obj[self._id_litteral]) def find_all_count(self, query, projection, search, sort, page_size=0, page_num=0): if not _validate_query(query): raise QueryError("Query must be a dictionary and Query must have a specific structure") if not _validate_projection(projection): raise ProjectionError("Projection must be a dictionary and Projection must have a specific structure") if not _validate_search(search): raise SearchError("Search query must be a dictionary and Search query must have a specific structure") if not _validate_sort(sort): raise SortError("Sort query must be a dictionary and Sort query must have a specific structure") if not _validate_page_value(page_size): raise PageSizeError("Page Size must be a integer greater then or equal 0") if not _validate_page_value(page_num): raise PageNumError("Page Num must be a integer greater then or equal 0") search = _search_processing(search) for field in search.iterkeys(): if field in query: query[field].update(search[field]) else: query[field] = search[field] objs = [] last_id = self._get_current_id(unchange=True) if not last_id: return objs, 0 for i in xrange(last_id): this_id = i + 1 key_start = '{}|{}'.format(self._id_litteral, this_id) key_end = '{}|{}||'.format(self._id_litteral, this_id) scan = self._cli.hscan(self.coll, key_start, key_end, -1) obj = self._get_obj_from_scan(scan, this_id) if obj and _is_match_query(obj, query): objs.append(_filter_field(obj, projection, id_litteral=self._id_litteral)) if sort: objs = _sorted_processing(objs, sort) count = len(objs) if page_size != 0: el_start = page_size*page_num el_end = el_start + page_size objs = objs[el_start:el_end] return objs, count def find_all(self, query, projection, search, sort, page_size=0, page_num=0): return self.find_all_count(query, projection, search, sort, page_size, page_num)[0] def find_one(self, query, projection): objs = self.find_all(query, projection, {}, []) if query and len(objs) == 1: obj = objs[0] else: obj = None return obj def update(self, obj): if not _validate_update(obj, id_litteral=self._id_litteral): raise DocumentError("Document must be a dictionary and Document must have a specific structure and Document must have the '{}' field".format(self._id_litteral)) if not self._check_id(obj[self._id_litteral]): raise DocumentError("'{}' field of Document is undefined in current collection".format(self._id_litteral)) return self._save_document(obj, obj[self._id_litteral]) def remove(self, query): objs = self.find_all(query, {}, {}, []) for obj in objs: id = obj[self._id_litteral] for key in obj.iterkeys(): key_db = '{}|{}|{}'.format(self._id_litteral, id, key) self._cli.hdel(self.coll, key_db) self._cli.hdel(self._coll_types, key_db) return len(objs) def count(self): return self.find_all_count({}, {}, {}, [])[1] def clear(self): count = self.count() self._cli.hclear(self.coll) self._cli.hclear(self._coll_types) return count