Source code for nosqlapi.docdb.odm
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# vim: se ts=4 et syn=python:
# created by: matteo.guadrini
# odm -- nosqlapi
#
# Copyright (C) 2022 Matteo Guadrini <matteo.guadrini@hotmail.it>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""ODM module for document NOSQL database."""
# region Imports
from functools import wraps
from json import dumps
from nosqlapi.common.odm import Uuid
from nosqlapi.kvdb.odm import Keyspace
# endregion
# region global variable
__all__ = ['Database', 'Collection', 'Document', 'Index', 'document']
# endregion
# region Classes
[docs]class Database(Keyspace):
"""Represents database"""
pass
[docs]class Collection:
"""Represents collection of documents"""
[docs] def __init__(self, name, *docs):
"""Collection object
:param name: Name of collection
:param docs: Documents like dict
"""
self.name = name
self._docs = []
if docs:
self._docs.extend(list(docs))
@property
def docs(self):
"""Documents of collection"""
return self._docs
[docs] def append(self, doc):
"""Append document to collection
:param doc: Documents like dict
:return: None
"""
self._docs.append(doc)
[docs] def pop(self, doc=-1):
"""Delete document from collection
:param doc: Number of document to remove
:return: None
"""
self._docs.pop(doc)
def __getitem__(self, item):
return self.docs[item]
def __setitem__(self, key, value):
self._docs[key] = value
def __delitem__(self, key):
del self._docs[key]
[docs] def __repr__(self):
return f'<{self.__class__.__name__} object, name={self.name}>'
[docs] def __str__(self):
return f'{self.docs}'
def __len__(self):
return len(self.docs)
def __iter__(self):
return (doc for doc in self.docs)
def __bool__(self):
return True if self.docs else False
[docs]class Document:
"""Represents document"""
[docs] def __init__(self, value=None, oid=None, **values):
"""Document object
:param value: Body of document like dict
:param oid: String id (default uuid1 string)
:param values: Additional values of body
"""
self._body = {}
if not oid:
self._id = Uuid()
self['_id'] = self.id.__str__()
else:
self._id = self['_id'] = oid
if value is not None or isinstance(value, dict):
self._body.update(value)
if values:
self._body.update(values)
@property
def id(self):
"""Document unique id"""
return self._id
@property
def body(self):
"""Elements of document"""
return self._body
@body.setter
def body(self, value):
"""Elements of document"""
if isinstance(value, dict):
self._body = value
else:
raise ValueError('value must be a dict')
[docs] def to_json(self, indent=2):
"""Transform document into json
:param indent: Number of indentation
:return: str
"""
return dumps(self.body, indent=indent)
def __getitem__(self, item):
return self.body[item]
def __setitem__(self, key, value):
self._body[key] = value
def __delitem__(self, key):
del self._body[key]
[docs] def __repr__(self):
return f'<{self.__class__.__name__} object, id={self.id}>'
[docs] def __str__(self):
return f'{self.body}'
def __len__(self):
return len(self.body)
def __iter__(self):
return (key for key in self.body)
[docs]class Index:
"""Represents document index"""
[docs] def __init__(self, name, data):
"""Index object
:param name: Name of index
:param data: Data of index like dict
"""
self.name = name
self.data = {}
self.data.update(data)
def __getitem__(self, item):
return self.data[item]
def __setitem__(self, key, value):
self.data[key] = value
def __delitem__(self, key):
del self.data[key]
[docs] def __str__(self):
return self.data.__str__()
[docs] def __repr__(self):
return f"Index({', '.join(f'{key}={value}' for key, value in self.data.items())})"
# endregion
# region Functions
[docs]def document(func):
"""Decorator function to transform dictionary object to Document object
:param func: function to decorate
:return: Document object
"""
@wraps(func)
def inner(*args, **kwargs):
data = func(*args, **kwargs)
if not isinstance(data, dict):
raise ValueError(f"function {func.__name__} doesn't return a dict")
return Document(value=data)
return inner
# endregion