Welcome to nosqlapi’s documentation!¶
This library is defined to encourage similarity between Python modules used to access NOSQL databases. In this way, I hope for consistency that leads to more easily understood modules, code that generally gets more portability across databases and a broader scope of database connectivity from Python. This document describes the Python NOSQL database API specification.
Supported NOSQL database types¶
NoSql databases are of four types:
Key/Value database
Column database
Document database
Graph database
For each type of database, nosqlapi offers standard interfaces, in order to unify as much as possible the names of methods and functions.
Key-Value database¶
A key–value database, or key–value store, is a data storage paradigm designed for storing, retrieving, and managing associative arrays, and a data structure more commonly known today as a dictionary or hash table. Dictionaries contain a collection of objects, or records, which in turn have many different fields within them, each containing data. These records are stored and retrieved using a key that uniquely identifies the record, and is used to find the data within the database.
Column database¶
A column-oriented DBMS or columnar DBMS is a database management system (DBMS) that stores data tables by column rather than by row. Practical use of a column store versus a row store differs little in the relational DBMS world. Both columnar and row databases can use traditional database query languages like SQL to load data and perform queries. Both row and columnar databases can become the backbone in a system to serve data for common extract, transform, load (ETL) and data visualization tools. However, by storing data in columns rather than rows, the database can more precisely access the data it needs to answer a query rather than scanning and discarding unwanted data in rows.
Document database¶
A document-oriented database, or document store, is a computer program and data storage system designed for storing, retrieving and managing document-oriented information, also known as semi-structured data. Document-oriented databases are one of the main categories of NoSQL databases, and the popularity of the term “document-oriented database” has grown with the use of the term NoSQL itself. Graph databases are similar, but add another layer, the relationship, which allows them to link documents for rapid traversal.
Graph database¶
Graph databases are a type of NoSQL database, created to address the limitations of relational databases. While the graph model explicitly lays out the dependencies between nodes of data, the relational model and other NoSQL database models link the data by implicit connections. In other words, relationships are a first-class citizen in a graph database and can be labelled, directed, and given properties. This is compared to relational approaches where these relationships are implied and must be reified at run-time. Graph databases are similar to 1970s network model databases in that both represent general graphs, but network-model databases operate at a lower level of abstraction and lack easy traversal over a chain of edges.
Interface¶
Access and CRUD operations in the various types of
databases are standardized in two objects: Connection
and Session
objects.
Constructors¶
Access to the database is made available through Connection
objects. The module must provide the following constructor for these:
>>> connection = Connection(*args, **kwargs)
The Connection
object allows you to access the database and operate directly at the database server level.
This object has a connect
method that returns a Session
object. It takes a number of parameters which are database dependent.
>>> session = connection.connect(*args, **kwargs)
With Session
object you can operate directly on the database data specified when creating the Connection
object.
Note
This division of objects allows you to have more flexibility in case you want to change databases. It is not necessary to create a new object to connect to a different database.
Globals¶
api_level
is a global variable to check compatibility with the names defined in this document. Currently the level is 1.0.
CONNECTION
is a global variable where to save a global Connection
object.
SESSION
is a global variable where to save a global Session
object.
Exceptions¶
All defined exceptions derive from the general exception Error
based on Exception
class, which tends not to be used directly.
>>> raise Error("Don't use this, but its subclasses!")
Name |
Base |
Description |
---|---|---|
|
|
Exception that is the base class of all other error exceptions. Use only for checks. |
|
|
Exception raised when an unspecified error occurred. |
|
|
Exception raised for errors that are related to the database connection. |
|
|
Exception raised for errors that are related to the database close connection. |
|
|
Exception raised for errors that are related to the database, generally. |
|
|
Exception raised for errors that are related to the creation of a database. |
|
|
Exception raised for errors that are related to the deletion of a database. |
|
|
Exception raised for errors that are related to the session, generally. |
|
|
Exception raised for errors that are related to the inserting data on a database session. |
|
|
Exception raised for errors that are related to the updating data on a database session. |
|
|
Exception raised for errors that are related to the deletion data on a database session. |
|
|
Exception raised for errors that are related to the closing database session. |
|
|
Exception raised for errors that are related to the finding data on a database session. |
|
|
Exception raised for errors that are related to the grant or revoke permission on a database. |
|
|
Exception raised for errors that are related to the selectors in general. |
|
|
Exception raised for errors that are related to the selectors attribute. |
The tree of exceptions:
Exception
|__Error
|__UnknownError
|__ConnectError
|__CloseError
|__DatabaseError
| |__DatabaseCreationError
| |__DatabaseDeletionError
|__SessionError
| |__SessionInsertingError
| |__SessionUpdatingError
| |__SessionDeletingError
| |__SessionClosingError
| |__SessionFindingError
| |__SessionACLError
|__SelectorError
|__SelectorAttributeError
Selectors¶
NOSQL databases do not use SQL syntax, or if they do, it is encapsulated in a shell or interpreter.
The selection queries will be driven through Selector
objects which will then be passed to the find
method of a Session
object.
>>> selector = Selector(*args, **kwargs)
>>> session.find(selector)
Note
A string representing the language of the selector can also be passed to the find method.
API¶
The following describes the object APIs that will build a connection to databases. All the method names that are present below are common to all Nosql database types.
Note
Some methods are specific to certain types of databases, so they will not be present in all objects.
Connection Objects¶
The Connection
object creates a layer concerning the connection to the server.
You can not specify any specific databases; in this way, the object will be used for the creation and management of all databases at the server level.
Connection attributes¶
- connected¶
This read-only attribute contains a boolean value representing whether the connection has been established with the server.
Connection methods¶
- close(*args, **kwargs)¶
Immediate closure of the connection and session with the database. Returns None
.
- connect(*args, **kwargs)¶
Create a persistent session with a specific database. Returns an object of type Session
.
Attention
A valid Session
object must necessarily be instantiated with a valid database name.
- create_database(*args, **kwargs)¶
Create a single database. Returns an object of type Response
.
- has_database(*args, **kwargs)¶
Check if exists a single database. Returns an object of type Response
.
- delete_database(*args, **kwargs)¶
Delete of a single database. Returns an object of type Response
.
- databases(*args, **kwargs)¶
List all databases. Returns an object of type Response
.
- show_database(*args, **kwargs)¶
Show an information of a specific database. Returns an object of type Response
.
Session Objects¶
The Session
object represents the session to a database. With this object it is possible to operate directly on the data.
Warning
This object is derived from the connect()
method of a Connection
object. It is also possible to instantiate the session
object through a connect function, but it will be mandatory to specify a database.
Session attributes¶
- connection¶
Connection
or other compliant object of server in current session
- description¶
This read-only attribute contains the session parameters (can be str
, tuple
or dict
).
- item_count¶
This read-only attribute contains the number of object returned of an operations (must be int
).
- database¶
This read-only attribute contains the name of database in current session (must be str
).
- acl¶
This read-only attribute contains the Access Control List in the current session (must be tuple
, dict
or Response
object).
- indexes¶
This read-only attribute contains the name of indexes of the current database (must be tuple
, dict
or Response
object).
Session methods¶
- get(*args, **kwargs)¶
Get one or more data from specific database. Returns an object of type Response
.
Attention
This is the letter R of CRUD operations.
- insert(*args, **kwargs)¶
Insert one data on specific database. Returns an object of type Response
.
Attention
This is the letter C of CRUD operations.
- insert_many(*args, **kwargs)¶
Insert one or more data on specific database. Returns an object of type Response
.
- update(*args, **kwargs)¶
Update one existing data on specific database. Returns an object of type Response
.
Attention
This is the letter U of CRUD operations.
- update_many(*args, **kwargs)¶
Update one or more existing data on specific database. Returns an object of type Response
.
- delete(*args, **kwargs)¶
Delete one existing data on specific database. Returns an object of type Response
.
Attention
This is the letter D of CRUD operations.
- close(*args, **kwargs)
Close immediately current session. Returns None
.
- find(*args, **kwargs)¶
Find data on specific database with str
selector or Selector
object. Returns an object of type Response
.
- grant(*args, **kwargs)¶
Grant Access Control List on specific database. Returns an object of type Response
.
- revoke(*args, **kwargs)¶
Revoke Access Control List on specific database. Returns an object of type Response
- new_user(*args, **kwargs)¶
Create new normal or admin user. Returns an object of type Response
- set_user(*args, **kwargs)¶
Modify exists user or reset password. Returns an object of type Response
- delete_user(*args, **kwargs)¶
Delete exists user. Returns an object of type Response
- add_index(*args, **kwargs)¶
Add a new index to database. Returns an object of type Response
- delete_index(*args, **kwargs)¶
Delete exists index to database. Returns an object of type Response
- call(batch, *args, **kwargs)¶
Call a Batch
object to execute one or more statement. Returns an object of type Response
Selector Objects¶
The Selector
object represents the query string for find data from database.
Once instantiated, it can be an input to the Session
’s find method.
Selector attributes¶
- selector¶
This read/write attribute represents the selector key/value than you want search.
- fields¶
This read/write attribute represents the fields key that returned from find operations.
- partition¶
This read/write attribute represents the name of partition/collection in a database.
- condition¶
This read/write attribute represents other condition to apply a selectors.
- order¶
This read/write attribute represents order returned from find operations.
- limit¶
This read/write attribute represents limit number of objects returned from find operations.
Selector methods¶
- build(*args, **kwargs)¶
Building a selector string in the dialect of a NOSQL language based on various property of the Selector
object. Returns str
.
Response Objects¶
The Response
object represents the response that comes from an operation from the database.
Note
Response
objects is a species of an either-data type, because contains both success and error values.
Response attributes¶
- data¶
This read-only attribute represents the effective python data (Any
) than returned from an operation from the database.
- code¶
This read-only attribute represents a number code (int
) of error or success in an operation.
- header¶
This read-only attribute represents a str
information (header) of an operation.
- error¶
This read-only attribute represents a str
or Exception
object error of an operation.
- dict¶
This read-only attribute represents a dict
transformation of Response
object.
Response methods¶
- throw()¶
Raise exception stored in error
property.
Batch Objects¶
The Batch
object represents an aggregation of CRUD operations.
Batch attributes¶
- session¶
This read/write attribute represents a Session
object.
- batch¶
This read/write attribute represents a batch operation.
Batch methods¶
- execute(*args, **kwargs)¶
Executing a batch operation with position and keyword arguments. Returns tuple
or an object of type Response
nosqlapi package¶
The package nosqlapi is a collection of interface, utility class and functions for build your own NOSQL python package.
This library offers API-based interfaces described above, helping to build a more coherent and integrated python library for a NOSQL database, with names similar to another library using the same interfaces.
The benefit of using nosqlapi
is to standardize the names and syntax for the end user, so as to make as few changes as possible to your software.
Installation¶
To install the nosqlapi package, run pip
as follows:
$ pip install nosqlapi #from pypi
$ git clone https://github.com/MatteoGuadrini/nosqlapi.git #from official repo
$ cd nosqlapi
$ python setup.py install
nosqlapi common¶
In this package you will find the modules that contain common interfaces and ODMs (Object-Data Mapping), shared by all four types of NOSQL database.
In the core module, we find the abstract classes that form the basis of the classes of all four NOSQL database types.
Note
In theory, you shouldn’t use these classes to build your class module for the NOSQL database you plan to build, but you could; in all classes you have the necessary methods to iterate with the database server.
Module that contains the core objects.
- class nosqlapi.common.core.Batch(batch, session=None)[source]¶
Bases:
abc.ABC
Batch abstract class
The abstract class
Batch
is used to create batch-type classes that represent a collection of operations to be performed at the same time.- __init__(batch, session=None)[source]¶
Instantiate Batch object
- Parameters
batch – List of commands
session – Session object or other compliant object
- __weakref__¶
list of weak references to the object (if defined)
- property batch¶
String batch operation
- abstract execute(*args, **kwargs)[source]¶
Execute some batch statement
- Returns
Union[tuple, Response]
- property session¶
Session object
- class nosqlapi.common.core.Connection(host=None, user=None, password=None, database=None, port=None, bind_address=None, read_timeout=None, write_timeout=None, ssl=None, ssl_ca=None, ssl_cert=None, tls=None, ssl_key=None, ssl_verify_cert=None, max_allowed_packet=None)[source]¶
Bases:
abc.ABC
Server connection abstract class
The abstract class
Connection
is used to create connection-type classes that will allow you to work directly on the layer at the database level.- __init__(host=None, user=None, password=None, database=None, port=None, bind_address=None, read_timeout=None, write_timeout=None, ssl=None, ssl_ca=None, ssl_cert=None, tls=None, ssl_key=None, ssl_verify_cert=None, max_allowed_packet=None)[source]¶
Instantiate Connection object
- Parameters
host – Name of host that contains database
user – Username for connect to the host
password – Password for connect to the host
database – Name of database
port – Tcp port
bind_address – Hostname or an IP address for multiple network interfaces
read_timeout – Timeout for reading from the connection in seconds
write_timeout – Timeout for writing from the connection in seconds
ssl – Ssl connection established
ssl_ca – Ssl CA file specified
ssl_cert – Ssl certificate file specified
tls – Tls connection established
ssl_key – Ssl private key file specified
ssl_verify_cert – Verify certificate file
max_allowed_packet – Max size of packet sent to server in bytes
- __weakref__¶
list of weak references to the object (if defined)
- property connected¶
Boolean representing the database connection
- Returns
bool
- abstract create_database(*args, **kwargs)[source]¶
Create new database on server
- Returns
Union[bool, Response]
- abstract delete_database(*args, **kwargs)[source]¶
Delete database on server
- Returns
Union[bool, Response]
- class nosqlapi.common.core.Response(data, code=None, header=None, error=None)[source]¶
Bases:
abc.ABC
Server response abstract class
The abstract class
Response
is used to create response-type classes that represent response data to an operation.- __init__(data, code=None, header=None, error=None)[source]¶
Instantiate Response object
- Parameters
data – Data from operation
code – Exit code of operation
header – Header of operation
error – Error string or Exception class
- property code¶
Number code of error or success in an operation
- property data¶
The effective data than returned
- property dict¶
dict format for Response object
- property error¶
Error of an operation
- property header¶
Information (header) of an operation
- class nosqlapi.common.core.Selector(selector=None, fields=None, partition=None, condition=None, order=None, limit=None)[source]¶
Bases:
abc.ABC
Selector abstract class
The abstract class
Selector
is used to create selector-type classes, which represent a query in the specific language of the database.- __init__(selector=None, fields=None, partition=None, condition=None, order=None, limit=None)[source]¶
Instantiate Selector object
- Parameters
selector – Selector part of the query string
fields – Return fields
partition – Partition or collection of data
condition – Condition of query
order – Order by specific selector
limit – Limit result
- __weakref__¶
list of weak references to the object (if defined)
- property condition¶
Other condition to apply a selectors
- property fields¶
Key that returned from find operations
- property limit¶
Limit number of objects returned from find operations
- property order¶
Order returned from find operations
- property partition¶
The name of partition or collection in a database
- property selector¶
Value than you want search
- class nosqlapi.common.core.Session(connection, database=None)[source]¶
Bases:
abc.ABC
Server session abstract class
The abstract class
Session
is used to create session-type classes that will allow you to work directly on the layer at the data level.- __init__(connection, database=None)[source]¶
Instantiate Session object
- Parameters
connection – Connection object or other object to serve connection
database – database name
- __weakref__¶
list of weak references to the object (if defined)
- abstract property acl¶
Access Control List in the current session
- property connection¶
Connection of server in current session
- property database¶
Name of database in current session
- abstract delete_index(*args, **kwargs)[source]¶
Delete index to database
- Returns
Union[bool, Response]
- abstract property description¶
Contains the session parameters
- abstract property indexes¶
Name of indexes of the current database
- abstract insert_many(*args, **kwargs)[source]¶
Insert one or more value
- Returns
Union[bool, Response]
- abstract property item_count¶
Number of item returned from latest CRUD operation
Classes on this module are abstract (interfaces), therefore, they cannot be instantiated as objects, but inherit them in their own classes.
# mymodule.py
import nosqlapi
# this module is my library of NOSQL database
class Connection(nosqlapi.common.Connection):
def __init__(host=None, port=None, database=None, username=None, password=None, ssl=None, tls=None, cert=None,
ca_cert=None, ca_bundle=None): ...
def close(self, force=False): ...
def connect(self, retry=1): ...
def create_database(self, database, exists=False): ...
def has_database(self, database): ...
def databases(self, return_obj=None): ...
def delete_database(self, force=False): ...
def show_database(self, database): ...
conn = Connection('server.local', 1241, 'new_db', username='admin', password='pa$$w0rd', ssl=True)
conn.create_database(conn.database) # use 'database' attribute equals 'new_db'
if conn.has_database('new_db'): # check if database exists
sess = conn.connect() # Session object
else:
raise nosqlapi.common.exception.ConnectError(f'Connection error with database {conn.database}')
...
In the exception module, we find the exceptions listed in the exceptions table in the interface documentation.
Note
You should never use the Error exception class as it is the basis for all exceptions. If you have a generic or unknown error, use UnknownError instead.
Exception module.
This module contains the hierarchy of exceptions included in the NOSQL API.
- exception nosqlapi.common.exception.CloseError[source]¶
Bases:
nosqlapi.common.exception.ConnectError
Exception raised for errors that are related to the database close connection.
- exception nosqlapi.common.exception.ConnectError[source]¶
Bases:
nosqlapi.common.exception.Error
Exception raised for errors that are related to the database connection.
- exception nosqlapi.common.exception.DatabaseCreationError[source]¶
Bases:
nosqlapi.common.exception.DatabaseError
Exception raised for errors that are related to the creation of a database.
- exception nosqlapi.common.exception.DatabaseDeletionError[source]¶
Bases:
nosqlapi.common.exception.DatabaseError
Exception raised for errors that are related to the deletion of a database.
- exception nosqlapi.common.exception.DatabaseError[source]¶
Bases:
nosqlapi.common.exception.Error
Exception raised for errors that are related to the database, generally.
- exception nosqlapi.common.exception.Error[source]¶
Bases:
Exception
Error that is the base class of all other error exceptions. You can use this to catch all errors with one single except statement.
- exception nosqlapi.common.exception.SelectorAttributeError[source]¶
Bases:
nosqlapi.common.exception.SelectorError
Exception raised for errors that are related to the selectors attribute.
- exception nosqlapi.common.exception.SelectorError[source]¶
Bases:
nosqlapi.common.exception.Error
Exception raised for errors that are related to the selectors in general.
- exception nosqlapi.common.exception.SessionACLError[source]¶
Bases:
nosqlapi.common.exception.SessionError
Exception raised for errors that are related to the grant or revoke permission on a database.
- exception nosqlapi.common.exception.SessionClosingError[source]¶
Bases:
nosqlapi.common.exception.SessionError
Exception raised for errors that are related to the closing database session.
- exception nosqlapi.common.exception.SessionDeletingError[source]¶
Bases:
nosqlapi.common.exception.SessionError
Exception raised for errors that are related to the deletion data on a database session.
- exception nosqlapi.common.exception.SessionError[source]¶
Bases:
nosqlapi.common.exception.Error
Exception raised for errors that are related to the session, generally.
- exception nosqlapi.common.exception.SessionFindingError[source]¶
Bases:
nosqlapi.common.exception.SessionError
Exception raised for errors that are related to the finding data on a database session.
- exception nosqlapi.common.exception.SessionInsertingError[source]¶
Bases:
nosqlapi.common.exception.SessionError
Exception raised for errors that are related to the inserting data on a database session.
- exception nosqlapi.common.exception.SessionUpdatingError[source]¶
Bases:
nosqlapi.common.exception.SessionError
Exception raised for errors that are related to the updating data on a database session.
- exception nosqlapi.common.exception.UnknownError[source]¶
Bases:
nosqlapi.common.exception.Error
Exception raised when an unspecified error occurred.
The exceptions stated in this form are used in certain circumstances. See the table of exceptions to find out more.
import nosqlapi
raise nosqlapi.common.exception.UnknownError('I have no idea what the hell happened!')
raise nosqlapi.UnknownError('in short')
In the odm module, we find generic classes represent real objects present in various NOSQL databases.
Note
These objects are not mandatory for the implementation of their own NOSQL module. Rather they serve to help the end user have some consistency in python.
Module that contains some ODM common objects.
- nosqlapi.common.odm.Array¶
alias of
nosqlapi.common.odm.List
- class nosqlapi.common.odm.Ascii(value='')[source]¶
Bases:
str
Represents ASCII string
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Boolean(value)[source]¶
Bases:
object
Represents bool
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Counter(value=0)[source]¶
Bases:
object
Represents integer counter
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Date[source]¶
Bases:
datetime.date
Represents date in format %Y-%m-%d
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Decimal(value='0', context=None)[source]¶
Bases:
decimal.Decimal
Represents decimal number
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Double(x=0, /)[source]¶
Bases:
float
Represents float
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Duration[source]¶
Bases:
datetime.timedelta
Represents duration ISO 8601 format: P[n]Y[n]M[n]DT[n]H[n]M[n]S
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Float(x=0, /)[source]¶
Bases:
float
Represents float
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Inet(ip)[source]¶
Bases:
object
Represents ip address version 4 or 6 like string
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.List(iterable=(), /)[source]¶
Bases:
list
Represents list of objects
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Map[source]¶
Bases:
dict
Represents dict of objects
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Null[source]¶
Bases:
object
Represents None
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.SmallInt(number)[source]¶
Bases:
nosqlapi.common.odm.Int
Represents small integer: -32767 to 32767
- class nosqlapi.common.odm.Text[source]¶
Bases:
str
Represents str
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Time[source]¶
Bases:
datetime.time
Represents time
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Timestamp[source]¶
Bases:
datetime.datetime
Represents datetime timestamp
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.common.odm.Uuid[source]¶
Bases:
object
Represents uuid version 1
- __weakref__¶
list of weak references to the object (if defined)
- nosqlapi.common.odm.Varchar¶
alias of
nosqlapi.common.odm.Text
- nosqlapi.common.odm.Varint¶
alias of
nosqlapi.common.odm.Int
The classes within the module are python representations of real objects in the database.
import nosqlapi
null = nosqlapi.common.odm.Null() # compare with null object into database
null = nosqlapi.Null() # in short
map_ = nosqlapi.Map() # like dict
inet = nosqlapi.Inet('192.168.1.1') # ipv4/ipv6 addresses
In the utils module, we find classes and functions that help the end user’s work.
Note
The functions and classes in this module are meant for the work of the end user and not for those who build libraries.
Utils function and classes for any type of NOSQL database
- class nosqlapi.common.utils.Manager(connection, *args, **kwargs)[source]¶
Bases:
object
Manager class for api compliant nosql database connection
- __weakref__¶
list of weak references to the object (if defined)
- nosqlapi.common.utils.api(**methods)[source]¶
Decorator function to transform standard classes into API compliant classes
- Parameters
methods – method names that you want to bind to the methods of API compliant classes
- Returns
class
- nosqlapi.common.utils.apply_vendor(name)[source]¶
Apply new name of api name
- Parameters
name – name of vendor
- Returns
None
- nosqlapi.common.utils.cursor_response(resp)[source]¶
Transform nosql Response object to list of tuple, like a response of sql cursor object
- Parameters
resp – Response object or other compliant object
- Returns
List[tuple]
- nosqlapi.common.utils.global_session(connection, *args, **kwargs)[source]¶
Global session
- Parameters
connection – Connection object or other compliant object
args – positional arguments of connect method on Connection object
kwargs – keywords arguments of connect method on Connection object
- Returns
None
- nosqlapi.common.utils.response(data)[source]¶
Simple response with data only
- Parameters
data – Data
- Returns
Response
This is an example of a Manager
object, used to change manage multiple sessions to different types of databases.
import nosqlapi
import mymodule
import othermodule
connection = mymodule.Connection('server.local', 1241, 'new_db', username='admin', password='pa$$w0rd', ssl=True)
manager = nosqlapi.common.utils.Manager(connection)
# Change connection and session
new_connection = othermodule.Connection('server2.local', 1241, 'other_db', username='admin', password='pa$$w0rd', ssl=True)
manager.change(new_connection)
# use connection methods
manager.create_database('db')
manager.databases()
# use session methods
manager.acl
manager.get('key')
The api
decorator function allows you to return existing classes so that the methods can match the NOSQL api described in this documentation.
import nosqlapi
import pymongo
@nosqlapi.common.utils.api(database_names='databases', drop_database='delete_database', close_cursor='close')
class ApiConnection(pymongo.Connection): ...
connection = ApiConnection('localhost', 27017, 'test_database')
print(hasattr(connection, 'databases')) # True
The global_session
function allows you to instantiate a global connection and session.
import nosqlapi
import mymodule
connection = mymodule.Connection('server.local', 1241, 'new_db', username='admin', password='pa$$w0rd', ssl=True)
nosqlapi.common.utils.global_session(connection)
print(nosqlapi.CONNECTION) # Connection object
print(nosqlapi.SESSION) # Session object
The cursor_response
function allows you to convert a Response object into a classic list of tuples.
import nosqlapi
import mymodule
connection = mymodule.Connection('server.local', 1241, 'new_db', username='admin', password='pa$$w0rd', ssl=True)
resp = connection.databases()
print(resp) # Response object
print(cursor_response(resp)) # [('db1', 'db2')]
The apply_vendor
function allows you to rename representation object from nosqlapi to other name
>>> import nosqlapi
>>> class Response(nosqlapi.Response): ...
>>> resp = Response('some data')
>>> resp
<nosqlapi Response object>
>>> nosqlapi.apply_vendor('pymongo')
>>> resp
<pymongo Response object>
nosqlapi key-value¶
In this package we find abstract classes and ODM classes concerning the Key-Value database types.
The client module contains the specific classes for key-value databases and they are based on the core classes.
Client module for key-value NOSQL database.
- class nosqlapi.kvdb.client.KVBatch(batch, session=None)[source]¶
Bases:
nosqlapi.common.core.Batch
,abc.ABC
Key-value NOSQL database Batch class
- class nosqlapi.kvdb.client.KVConnection(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Connection
,abc.ABC
Key-value NOSQL database Connection class
- __init__(*args, **kwargs)[source]¶
Instantiate Connection object
- Parameters
host – Name of host that contains database
user – Username for connect to the host
password – Password for connect to the host
database – Name of database
port – Tcp port
bind_address – Hostname or an IP address for multiple network interfaces
read_timeout – Timeout for reading from the connection in seconds
write_timeout – Timeout for writing from the connection in seconds
ssl – Ssl connection established
ssl_ca – Ssl CA file specified
ssl_cert – Ssl certificate file specified
tls – Tls connection established
ssl_key – Ssl private key file specified
ssl_verify_cert – Verify certificate file
max_allowed_packet – Max size of packet sent to server in bytes
- class nosqlapi.kvdb.client.KVResponse(data, code=None, header=None, error=None)[source]¶
Bases:
nosqlapi.common.core.Response
,abc.ABC
Key-value NOSQL database Response class
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.kvdb.client.KVSelector(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Selector
,abc.ABC
Key-value NOSQL database Selector class
- __init__(*args, **kwargs)[source]¶
Instantiate Selector object
- Parameters
selector – Selector part of the query string
fields – Return fields
partition – Partition or collection of data
condition – Condition of query
order – Order by specific selector
limit – Limit result
- abstract first_greater_or_equal(key)[source]¶
First greater or equal key by selector key
- Parameters
key – key to search
- Returns
Union[str, list, tuple]
- abstract first_greater_than(key)[source]¶
First greater key by selector key
- Parameters
key – key to search
- Returns
Union[str, list, tuple]
- class nosqlapi.kvdb.client.KVSession(connection, database=None)[source]¶
Bases:
nosqlapi.common.core.Session
,abc.ABC
Key-value NOSQL database Session class
This is an example of a library for connecting to a redis server.
# mykvdb.py
import nosqlapi
# this module is my library of NOSQL key-value database, like Redis database
class Connection(nosqlapi.kvdb.KVConnection):
def __init__(host='localhost', port=6379, database=0, username=None, password=None, ssl=None, tls=None,
cert=None, ca_cert=None, ca_bundle=None): ...
def close(self): ...
def connect(self, *args, **kwargs): ...
def create_database(self):
raise nosqlapi.DatabaseCreationError('See your server configuration file.')
def has_database(self, database): ...
def databases(self): ...
def delete_database(self): ...
def show_database(self, database): ...
class Session(nosqlapi.kvdb.KVSession):
# define here all methods
pass
conn = Connection('myredis.local', password='pa$$w0rd')
print(conn.databases()) # (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
sess = conn.connect() # Session object
...
The odm module contains the specific object for key-value databases.
ODM module for key-value NOSQL database.
- class nosqlapi.kvdb.odm.ExpiredItem(key, value=None, ttl=None)[source]¶
Bases:
nosqlapi.kvdb.odm.Item
Represents Item object with ttl expired time
- __init__(key, value=None, ttl=None)[source]¶
ExpiredItem object
- Parameters
key – Key of item
value – Value of item
ttl – Time to live of item
- property ttl¶
Time to live of item
- class nosqlapi.kvdb.odm.Index(name, key)¶
Bases:
tuple
- __getnewargs__()¶
Return self as a plain tuple. Used by copy and pickle.
- static __new__(_cls, name, key)¶
Create new instance of Index(name, key)
- __repr__()¶
Return a nicely formatted representation string
- property key¶
Alias for field number 1
- property name¶
Alias for field number 0
- class nosqlapi.kvdb.odm.Item(key, value=None)[source]¶
Bases:
object
Represents key/value like a dictionary
- __weakref__¶
list of weak references to the object (if defined)
- property key¶
Key of item
- set(key, value=None)[source]¶
Set item
- Parameters
key – Key of item
value – Value of the key
- Returns
None
- property value¶
Value of the key
- class nosqlapi.kvdb.odm.Keyspace(name, exists=False)[source]¶
Bases:
object
Represents keyspace like database
- __init__(name, exists=False)[source]¶
Keyspace object
- Parameters
name – Name of keyspace
exists – Existing keyspace (default False)
- __weakref__¶
list of weak references to the object (if defined)
- property exists¶
Existence of keyspace
- property name¶
Name of keyspace
- pop(item=- 1)[source]¶
Remove item from the store
- Parameters
item – Index of item to remove
- Returns
None
- property store¶
List of object into keyspace
- class nosqlapi.kvdb.odm.Subspace(name, sub=None, sep='.')[source]¶
Bases:
nosqlapi.kvdb.odm.Keyspace
Represents subspace of the keyspace
- class nosqlapi.kvdb.odm.Transaction(commands=None)[source]¶
Bases:
object
Represents group of commands in a single step
- __weakref__¶
list of weak references to the object (if defined)
- add(command, index=- 1)[source]¶
Add command to commands list
- Parameters
command – Command string
index – Index to append command
- Returns
None
- property commands¶
Command list
These objects represent the respective key-value in databases.
import nosqlapi
transaction = nosqlapi.kvdb.odm.Transaction() # in short -> nosqlapi.kvdb.Transaction()
# Add commands
transaction.add('ACL LIST')
transaction.add('ACL DELUSER test')
# Remove commands
transaction.delete(1)
item = nosqlapi.kvdb.Item('key', 'value') # item key=value
nosqlapi column¶
In this package we find abstract classes and ODM classes concerning the column database types.
The client module contains the specific classes for column databases and they are based on the core classes.
Client module for column NOSQL database.
- class nosqlapi.columndb.client.ColumnBatch(batch, session=None)[source]¶
Bases:
nosqlapi.common.core.Batch
,abc.ABC
Column NOSQL database Batch class
- class nosqlapi.columndb.client.ColumnConnection(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Connection
,abc.ABC
Column NOSQL database Connection class
- __init__(*args, **kwargs)[source]¶
Instantiate Connection object
- Parameters
host – Name of host that contains database
user – Username for connect to the host
password – Password for connect to the host
database – Name of database
port – Tcp port
bind_address – Hostname or an IP address for multiple network interfaces
read_timeout – Timeout for reading from the connection in seconds
write_timeout – Timeout for writing from the connection in seconds
ssl – Ssl connection established
ssl_ca – Ssl CA file specified
ssl_cert – Ssl certificate file specified
tls – Tls connection established
ssl_key – Ssl private key file specified
ssl_verify_cert – Verify certificate file
max_allowed_packet – Max size of packet sent to server in bytes
- class nosqlapi.columndb.client.ColumnResponse(data, code=None, header=None, error=None)[source]¶
Bases:
nosqlapi.common.core.Response
,abc.ABC
Column NOSQL database Response class
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.columndb.client.ColumnSelector(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Selector
,abc.ABC
Column NOSQL database Selector class
- __init__(*args, **kwargs)[source]¶
Instantiate Selector object
- Parameters
selector – Selector part of the query string
fields – Return fields
partition – Partition or collection of data
condition – Condition of query
order – Order by specific selector
limit – Limit result
- property filtering¶
Filter data
- class nosqlapi.columndb.client.ColumnSession(connection, database=None)[source]¶
Bases:
nosqlapi.common.core.Session
,abc.ABC
Column NOSQL database Session class
This is an example of a library for connecting to a cassandra server.
# mycolumndb.py
import nosqlapi
# this module is my library of NOSQL column database, like Cassandra database
class Connection(nosqlapi.columndb.ColumnConnection):
def __init__(host='localhost', port=7000, database=0, username=None, password=None, ssl=None, tls=None,
cert=None, ca_cert=None, ca_bundle=None): ...
def close(self): ...
def connect(self, *args, **kwargs): ...
def create_database(self, database): ...
def has_database(self, database): ...
def databases(self): ...
def delete_database(self): ...
def show_database(self, database): ...
class Session(nosqlapi.columndb.ColumnSession):
# define here all methods
pass
conn = Connection('mycassandra.local', 'testdb', username='admin', password='pa$$w0rd', tls=True)
print(conn.show_database('testdb') # ('testdb', 17983788, 123, False) -- name, size, rows, readonly
sess = conn.connect() # Session object
...
The odm module contains the specific object for column databases.
ODM module for column NOSQL database.
- class nosqlapi.columndb.odm.Column(name, data=None, of_type=None, max_len=None, auto_increment=False, primary_key=False, default=None)[source]¶
Bases:
object
Represents column as container of values
- __init__(name, data=None, of_type=None, max_len=None, auto_increment=False, primary_key=False, default=None)[source]¶
Column object
- Parameters
name – Name of column
:param data. Data list or tuple :param of_type: Type of column :param max_len: Max length of column :param auto_increment: Boolean value (default False) :param primary_key: Set this column like a primary key :param default: Default function for generate data
- __weakref__¶
list of weak references to the object (if defined)
- append(data=None)[source]¶
Appending data to column. If auto_increment is True, the value is incremented automatically.
- Parameters
data – Any type of data
- Returns
None
- property auto_increment¶
Auto-increment value
- property data¶
List of values
- property of_type¶
Type of column
- class nosqlapi.columndb.odm.Index(name, table, column)¶
Bases:
tuple
- __getnewargs__()¶
Return self as a plain tuple. Used by copy and pickle.
- static __new__(_cls, name, table, column)¶
Create new instance of Index(name, table, column)
- __repr__()¶
Return a nicely formatted representation string
- property column¶
Alias for field number 2
- property name¶
Alias for field number 0
- property table¶
Alias for field number 1
- class nosqlapi.columndb.odm.Keyspace(name, exists=False)[source]¶
Bases:
nosqlapi.kvdb.odm.Keyspace
Represents keyspace like database
- class nosqlapi.columndb.odm.Table(name, *columns, **options)[source]¶
Bases:
object
Represents table as container of columns
- __init__(name, *columns, **options)[source]¶
Table object
- Parameters
name – Name of table
columns – Columns
options – Options
- __weakref__¶
list of weak references to the object (if defined)
- add_column(*columns)[source]¶
Adding one or more column object to table
- Parameters
columns – Column objects
- Returns
None
- add_index(index)[source]¶
Adding index to index property
- Parameters
index – Name or Index object
- Returns
None
- add_row(*rows)[source]¶
Add one or more row into columns
- Parameters
rows – Tuple of objects
- Returns
None
- property columns¶
List of columns
- delete_column(index=- 1)[source]¶
Deleting one column to table
- Parameters
index – Number of index
- Returns
None
- delete_index(index=- 1)[source]¶
Deleting index to index property
- Parameters
index – Name or Index object
- Returns
None
- property index¶
List of indexes
- property name¶
Name of table
- property options¶
Options
- nosqlapi.columndb.odm.column(func)[source]¶
Decorator function to transform list or tuple object to Column object
- Parameters
func – function to decorate
- Returns
Column object
These objects represent the respective column in databases.
import nosqlapi
import mycolumndb
keyspace = nosqlapi.columndb.odm.Keyspace('new_db') # in short -> nosqlapi.columndb.Keyspace('new_db')
# Make columns
id = nosqlapi.columndb.Column('id', of_type=int)
id.auto_increment = True # increment automatically
name = nosqlapi.columndb.Column('name', of_type=str)
# Set data
id.append()
name.append('Matteo Guadrini')
# Make table
table = nosqlapi.columndb.Table('peoples', id, name)
# Create Column object from column decorator
@nosqlapi.columndb.column
def salary(file, limit=5000):
return [line
for line in open(file, 'rt').readlines()
if int(line) < 5000]
# Add column to table
table.add_column(id, name, salary('/tmp/salary.log'))
# Add table to keyspace
keyspace.append(table)
# Create database and insert data
mycolumndb.conn.create_database(keyspace)
mycolumndb.sess.create_table(table)
# Insert new data
mycolumndb.sess.insert('people', (None, 'Arthur Dent', 4000))
nosqlapi document¶
In this package we find abstract classes and ODM classes concerning the document database types.
The client module contains the specific classes for document databases and they are based on the core classes.
Client module for document NOSQL database.
- class nosqlapi.docdb.client.DocBatch(batch, session=None)[source]¶
Bases:
nosqlapi.common.core.Batch
,abc.ABC
Document NOSQL database Batch class
- class nosqlapi.docdb.client.DocConnection(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Connection
,abc.ABC
Document NOSQL database Connection class
- __init__(*args, **kwargs)[source]¶
Instantiate Connection object
- Parameters
host – Name of host that contains database
user – Username for connect to the host
password – Password for connect to the host
database – Name of database
port – Tcp port
bind_address – Hostname or an IP address for multiple network interfaces
read_timeout – Timeout for reading from the connection in seconds
write_timeout – Timeout for writing from the connection in seconds
ssl – Ssl connection established
ssl_ca – Ssl CA file specified
ssl_cert – Ssl certificate file specified
tls – Tls connection established
ssl_key – Ssl private key file specified
ssl_verify_cert – Verify certificate file
max_allowed_packet – Max size of packet sent to server in bytes
- class nosqlapi.docdb.client.DocResponse(data, code=None, header=None, error=None)[source]¶
Bases:
nosqlapi.common.core.Response
,abc.ABC
Document NOSQL database Response class
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.docdb.client.DocSelector(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Selector
,abc.ABC
Document NOSQL database Selector class
- class nosqlapi.docdb.client.DocSession(connection, database=None)[source]¶
Bases:
nosqlapi.common.core.Session
,abc.ABC
Document NOSQL database Session class
This is an example of a library for connecting to a mongodb server.
# mydocdb.py
import nosqlapi
# this module is my library of NOSQL document database, like MongoDB database
class Connection(nosqlapi.docdb.DocConnection):
def __init__(host='localhost', port=27017, database=None, username=None, password=None, ssl=None, tls=None,
cert=None, ca_cert=None, ca_bundle=None): ...
def close(self): ...
def connect(self, *args, **kwargs): ...
def create_database(self, database): ...
def has_database(self, database): ...
def databases(self): ...
def delete_database(self): ...
def show_database(self, database): ...
def copy_database(self, source, destination, force=False): ...
class Session(nosqlapi.docdb.DocSession):
# define here all methods
pass
conn = Connection('mymongo.local', username='admin', password='pa$$w0rd')
print(conn.databases()) # {"databases": [{"name": "admin", "sizeOnDisk": 978944, "empty": False}], "totalSize": 1835008, "ok": 1}
sess = conn.connect() # Session object
...
The odm module contains the specific object for document databases.
ODM module for document NOSQL database.
- class nosqlapi.docdb.odm.Collection(name, *docs)[source]¶
Bases:
object
Represents collection of documents
- __init__(name, *docs)[source]¶
Collection object
- Parameters
name – Name of collection
docs – Documents like dict
- __weakref__¶
list of weak references to the object (if defined)
- append(doc)[source]¶
Append document to collection
- Parameters
doc – Documents like dict
- Returns
None
- property docs¶
Documents of collection
- class nosqlapi.docdb.odm.Database(name, exists=False)[source]¶
Bases:
nosqlapi.kvdb.odm.Keyspace
Represents database
- class nosqlapi.docdb.odm.Document(value=None, oid=None, **values)[source]¶
Bases:
object
Represents document
- __init__(value=None, oid=None, **values)[source]¶
Document object
- Parameters
value – Body of document like dict
oid – String id (default uuid1 string)
values – Additional values of body
- __weakref__¶
list of weak references to the object (if defined)
- property body¶
Elements of document
- property id¶
Document unique id
- class nosqlapi.docdb.odm.Index(name, data)[source]¶
Bases:
object
Represents document index
- __init__(name, data)[source]¶
Index object
- Parameters
name – Name of index
data – Data of index like dict
- __weakref__¶
list of weak references to the object (if defined)
- nosqlapi.docdb.odm.document(func)[source]¶
Decorator function to transform dictionary object to Document object
- Parameters
func – function to decorate
- Returns
Document object
These objects represent the respective document in databases.
import nosqlapi
import mydocdb
# Create database
db = nosqlapi.docdb.odm.Database('test')
# Create documents
doc1 = nosqlapi.docdb.Document(oid=1)
doc2 = nosqlapi.docdb.Document(oid=2)
# Add documents to database
db.append(doc1)
db.append(doc2)
# Create Document object from document decorator
@nosqlapi.docdb.document
def user(name, age, salary):
return {'name': name, 'age': age, 'salary': salary}
# Create database with docs
mydocdb.conn.create_database(db)
# Add more doc
mydocdb.sess.insert(user('Matteo Guadrini', 36, 25000))
nosqlapi graph¶
In this package we find abstract classes and ODM classes concerning the graph database types.
The client module contains the specific classes for graph databases and they are based on the core classes.
Client module for graph NOSQL database.
- class nosqlapi.graphdb.client.GraphBatch(batch, session=None)[source]¶
Bases:
nosqlapi.common.core.Batch
,abc.ABC
Batch NOSQL database Session class
- class nosqlapi.graphdb.client.GraphConnection(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Connection
,abc.ABC
Graph NOSQL database Connection class
- __init__(*args, **kwargs)[source]¶
Instantiate Connection object
- Parameters
host – Name of host that contains database
user – Username for connect to the host
password – Password for connect to the host
database – Name of database
port – Tcp port
bind_address – Hostname or an IP address for multiple network interfaces
read_timeout – Timeout for reading from the connection in seconds
write_timeout – Timeout for writing from the connection in seconds
ssl – Ssl connection established
ssl_ca – Ssl CA file specified
ssl_cert – Ssl certificate file specified
tls – Tls connection established
ssl_key – Ssl private key file specified
ssl_verify_cert – Verify certificate file
max_allowed_packet – Max size of packet sent to server in bytes
- class nosqlapi.graphdb.client.GraphResponse(data, code=None, header=None, error=None)[source]¶
Bases:
nosqlapi.common.core.Response
,abc.ABC
Response NOSQL database Session class
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.graphdb.client.GraphSelector(*args, **kwargs)[source]¶
Bases:
nosqlapi.common.core.Selector
,abc.ABC
Graph NOSQL database Selector class
- class nosqlapi.graphdb.client.GraphSession(connection, database=None)[source]¶
Bases:
nosqlapi.common.core.Session
,abc.ABC
Graph NOSQL database Session class
This is an example of a library for connecting to a neo4j server.
# mygraphdb.py
import nosqlapi
# this module is my library of NOSQL graph database, like Neo4J database
class Connection(nosqlapi.graphdb.GraphConnection):
def __init__(host='localhost', port=7474, database=None, username=None, password=None, ssl=None, tls=None,
cert=None, ca_cert=None, ca_bundle=None): ...
def close(self): ...
def connect(self, *args, **kwargs): ...
def create_database(self, database): ...
def has_database(self, database): ...
def databases(self): ...
def delete_database(self): ...
def show_database(self, database): ...
class Session(nosqlapi.graphdb.GraphSession):
# define here all methods
pass
conn = Connection('myneo.local', 'mydb', username='admin', password='pa$$w0rd')
print(conn.show_database('mydb')) # {"name": "mydb", "address": "myneo.local:7474", "role": "standalone", "requestedStatus": "online", "currentStatus": "online", "error": "", "default": False}
sess = conn.connect() # Session object
...
The odm module contains the specific object for graph databases.
ODM module for graph NOSQL database.
- class nosqlapi.graphdb.odm.Database(name, address=None, role=None, status='online', default=False)[source]¶
Bases:
nosqlapi.kvdb.odm.Keyspace
Represents database
- __init__(name, address=None, role=None, status='online', default=False)[source]¶
Database object
- Parameters
name – Name of database
address – Address of database server
role – Role of database
status – Status of database (default online)
default – Default value or function
- property online¶
Status of database
- class nosqlapi.graphdb.odm.Index(name, node, properties, options)¶
Bases:
tuple
- __getnewargs__()¶
Return self as a plain tuple. Used by copy and pickle.
- static __new__(_cls, name, node, properties, options=None)¶
Create new instance of Index(name, node, properties, options)
- __repr__()¶
Return a nicely formatted representation string
- property name¶
Alias for field number 0
- property node¶
Alias for field number 1
- property options¶
Alias for field number 3
- property properties¶
Alias for field number 2
- class nosqlapi.graphdb.odm.Label[source]¶
Bases:
nosqlapi.common.odm.Text
Label of node
- class nosqlapi.graphdb.odm.Node(labels, properties=None, var='')[source]¶
Bases:
object
Represents node
- __init__(labels, properties=None, var='')[source]¶
Node object
- Parameters
labels – Label of node
properties – Properties of Node
var – Name variable
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.graphdb.odm.Property[source]¶
Bases:
dict
Property of node
- __weakref__¶
list of weak references to the object (if defined)
- class nosqlapi.graphdb.odm.Relationship(labels, properties=None, var='')[source]¶
Bases:
nosqlapi.graphdb.odm.Node
Represents relationship among nodes
- class nosqlapi.graphdb.odm.RelationshipType[source]¶
Bases:
nosqlapi.graphdb.odm.Label
Type of relationship like a label
- nosqlapi.graphdb.odm.node(func)[source]¶
Decorator function to transform dictionary object to Node object
- Parameters
func – function to decorate
- Returns
Node object
- nosqlapi.graphdb.odm.prop(func)[source]¶
Decorator function to transform dictionary object to Property object
- Parameters
func – function to decorate
- Returns
Property object
These objects represent the respective graph in databases.
import nosqlapi
import mygraphdb
# Create database
db = nosqlapi.graphdb.odm.Database('test')
# Create nodes
node1 = nosqlapi.graphdb.Node(var='n1', labels=[nosqlapi.graphdb.Label('Person')],
properties=nosqlapi.graphdb.Property({'name': 'Matteo', 'age': 35}))
node2 = nosqlapi.graphdb.Node(var='n2', labels=[nosqlapi.graphdb.Label('Person')],
properties=nosqlapi.graphdb.Property({'name': 'Julio', 'age': 53}))
# Add nodes to database
db.append(node1)
db.append(node2)
# Create Node object from node decorator
@nosqlapi.graphdb.node
def Person(name, age):
return {'name': name, 'age': age}
# Create Property object from prop decorator
@nosqlapi.graphdb.prop
def user(name, age):
return {'name': name, 'age': age}
# Create database with nodes
mydocdb.conn.create_database(db)
# Add other nodes
mydocdb.sess.insert(label='Person', properties=user('Matteo Guadrini', 36))
mydocdb.sess.insert(Person('Julio Artes', 29)) # Labels = ['Person']
Build a library¶
In this section we will build a NOSQL API compliant library, using the nosqlapi
library.
We will first build the core objects that will allow us to connect and operate with our database.
Note
The following example is designed for a NOSQL database of the Document type; by changing the application logic of the various methods in the classes you can build a library for another type of NOSQL database in the same way. The procedures are the same.
Warning
The purpose of this document is to explain how to use API class interfaces in the real world. Do not use the following example as a production library because it is very simplified and does not reflect all possible operations on a CouchDB server.
Let’s prepare the steps:
Create a package (directory with an
__init__.py
file) named pycouch.write
core.py
, with core classes and functions.write
utils.py
, with utility classes and function, like ODM objects.
Core¶
The core classes must provide a connection and operation interface towards the database. All CRUD operations will need to be supported. We can implement new ones, based on the type of database we support.
Create a core.py
module.
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# core.py
"""Python library for document CouchDB server"""
import nosqlapi
import urllib.request
import json
Connection class¶
Let’s build the server connection class. Since we are connecting to a CouchDB database, we will take advantage of the http API.
We define the __init__
constructor with all the info needed to perform operations with the database and create a Session
object using the connect()
method.
class Connection(nosqlapi.DocConnection):
"""CouchDB connection class; connect to CouchDB server."""
def __init__(self, host='localhost', port=5984, username=None, password=None, ssl=None, tls=None, cert=None,
database=None, ca_cert=None, ca_bundle=None):
super().__init__(self, host=host, port=port, username=username, password=password, ssl=ssl, tls=tls,
cert=cert, ca_cert=ca_cert, ca_bundle=ca_bundle)
self.method = 'https://' if self.port == 6984 or self.port == 443 else 'http://'
if self.username and self.password:
auth = f'{self.username}:{self.password}@'
self.url = f'{self.method}{auth}{self.host}:{self.port}'
Now let’s define the close
and connect
methods, to create the database connection.
def close(self, clean=False):
self._connected = False
if clean:
self.database = None
self.host = None
self.url = None
def connect(self):
session_url = self.url + f'/{self.database}'
if urllib.request.head(session_url).status_code == 200:
session = Session(connection=self, database=session_url)
self._connected = True
return session
else:
raise nosqlapi.ConnectError(f'I cannot connect to the server: {self.url}')
Now let’s all define methods that operate at the database level.
def create_database(self, name, shards=8, replicas=3, partitioned=False):
data = {"w": shards, "n": replicas}
if partitioned:
data['partitioned'] = partitioned
req = urllib.request.Request(self.url + f'/{name}',
data=json.dumps(data).encode('utf8'),
method='PUT')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.DatabaseCreationError(f'Database creation unsuccessfully: {name}'),
header=response.header_items())
def has_database(self, name):
response = urllib.request.urlopen(self.url + f'/{name}')
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.DatabaseError(f'Database not found: {name}'),
header=response.header_items())
def delete_database(self, name):
req = urllib.request.Request(self.url + f'/{name}', method='DELETE')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=bool(json.loads(response.read())),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.DatabaseDeletionError(f'Database deletion unsuccessfully: {name}'),
header=response.header_items())
def databases(self):
response = urllib.request.urlopen(self.url + '/_all_dbs')
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.DatabaseError(f'Database not found: {name}'),
header=response.header_items())
def show_database(self, name):
response = urllib.request.urlopen(self.url + f'/{name}')
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.DatabaseError(f'Database not found: {name}'),
header=response.header_items())
def copy_database(self, source, target, host, user=None, password=None, create_target=True, continuous=True):
data = {
"_id": f"{source}to{target}",
"source": source,
"target": {
"url": target,
"auth": {
"basic": {
"username": f"{user}",
"password": f"{password}"
}
}
},
"create_target": create_target,
"continuous": continuous
}
req = urllib.request.Request(self.url + '/_replicator', data=json.dumps(data).encode('utf8'), method='POST')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.DatabaseError(f'Database copy error: {name}'),
header=response.header_items())
Response class¶
There isn’t much to do with the Response
class. We inherit directly from the nosqlapi.docdb.DocResponse
class.
class Response(nosqlapi.DocResponse):
"""CouchDB response class; information about a certain transaction."""
...
Session class¶
Ok, now build the Session
class. This class used for CRUD operation on the specific database.
The acl
property is used to retrieve the Access Control lists of the current database and therefore the read/write permissions of the current session.
The property indexes
is used to retrieve all the indexes created for the current database.
class Session(nosqlapi.DocSession):
"""CouchDB session class; CRUD operation on the database."""
@property
def acl(self):
response = urllib.request.urlopen(self.database + f'/_security')
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionACLError(f'ACLs error'),
header=response.header_items())
@property
def indexes(self):
response = urllib.request.urlopen(self.database + f'/_index')
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionError(f'Index error'),
header=response.header_items())
Now let’s define the item_count
and description
properties. item_count
will be used to indicate a counter of each CRUD
operation that will be impacted. description
instead will contain the database info.
@property
def item_count(self):
return self._item_count
@property
def description(self):
response = urllib.request.urlopen(self.database + f'/_index')
if response.status_code == 200:
return self._description
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionError(f'Get description failed'),
header=response.header_items())
Now let’s all define CRUD (Create, Read, Update, Delete) methods. Create word is associated to insert
and insert_many
methods;
Read to get
method, Update to update
and update_many
methods and Delete to delete
method.
Each CRUD method is created to directly manage the data in the database to which the connection was created via the Connection
object.
def get(self, document='_all_docs', rev=None, attachment=None, partition=None, local=False, key=None):
url = self.database
if partition:
url += url + f'/{partition}/{document}'
else:
url += url + f'/{document}'
if attachment:
url += url + f'/{attachment}'
if rev:
url += url + f'?rev={rev}'
elif key:
url += url + f'?key={key}'
if bool(local):
url = self.database + f'/_local_docs'
response = urllib.request.urlopen(url)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionFindingError(f'Document not found: {url}'),
header=response.header_items())
def insert(self, name, data=None, attachment=None, partition=None):
url = self.database + f'/{name}'
if attachment:
url += f"/{attachment}"
id = f"{partition}:{name}" if partition else name
data = {"_id": id}
if data:
data.update(data)
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='PUT')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionInsertingError(f'Insert document {url} with data {data} failed'),
header=response.header_items())
def insert_many(self, *documents):
url = f"{self.database}/_bulk_docs"
data = {"docs": []}
if documents:
data['docs'].extend(documents)
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='POST')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionInsertingError('Bulk insert document failed'),
header=response.header_items())
def update(self, name, rev, data=None, partition=None):
url = self.database + f'/{name}?rev={rev}'
id = f"{partition}:{name}" if partition else name
data = {"_id": id}
if data:
data.update(data)
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='PUT')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionUpdatingError(f'Update document {url} with data {data} failed'),
header=response.header_items())
def update_many(self, *documents):
url = f"{self.database}/_bulk_docs"
data = {"docs": []}
if documents:
data['docs'].extend(documents)
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='POST')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionUpdatingError('Bulk update document failed'),
header=response.header_items())
def delete(self, name, rev, partition=None):
url = self.database + f'/{name}?rev={rev}'
id = f"{partition}:{name}" if partition else name
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='DELETE')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionDeletingError(f'Delete document {name} failed'),
header=response.header_items())
The close
method will only close the session, but not the connection.
def close(self):
self.database = None
self._connection = None
The find
method is the one that allows searching for data in the database.
This method can accept strings or Selector
objects, which help in the construction of the query in the database language.
def find(self, query):
url = f"{self.database}/_find"
if isinstance(query, Selector):
query = query.build()
req = urllib.request.Request(url,
data=query.encode('utf8'),
method='POST')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionFindingError(f'Find documents failed: {json.loads(response.read())}'),
header=response.header_items())
The grant
and revoke
methods are specific for enabling and revoking permissions on the current database.
def grant(self, admins, members):
url = f"{self.database}/_security"
data = {"admins": admins, "members": members}
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='PUT')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionACLError(f'Grant failed: {json.loads(response.read())}'),
header=response.header_items())
def revoke(self):
url = f"{self.database}/_security"
data = {"admins": {"names": [], "roles": []}, "members": {"names": [], "roles": []}}
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='PUT')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionACLError(f'Revoke failed: {json.loads(response.read())}'),
header=response.header_items())
Now let’s write the three methods for creating, modifying (also password reset) and deleting a user respectively: new_user
, set_user
and delete_user
.
def new_user(self, name, password, roles=None, type='user'):
if roles is None:
roles = []
server = self.database.split('/')
url = f"{server[0]}//{server[2]}/_users/org.couchdb.user:{name}"
data = {"name": name, "password": password, "roles": roles, "type": type}
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='PUT')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionACLError(f'New user failed: {json.loads(response.read())}'),
header=response.header_items())
def set_user(self, name, password, rev, roles=None, type='user'):
if roles is None:
roles = []
server = self.database.split('/')
url = f"{server[0]}//{server[2]}/_users/org.couchdb.user:{name}"
data = {"name": name, "password": password, "roles": roles, "type": type}
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='PUT')
req.add_header('Content-Type', 'application/json')
req.add_header(f"If-Match: {rev}")
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionACLError(f'Modify user or password failed: {json.loads(response.read())}'),
header=response.header_items())
def delete_user(self, name, rev, admin=False):
server = self.database.split('/')
if admin:
url = f"{server[0]}//{server[2]}/_users/org.couchdb.user:{name}"
else:
url = f"{server[0]}//{server[2]}/_config/admins/{name}"
req = urllib.request.Request(url, method='DELETE')
req.add_header('Content-Type', 'application/json')
req.add_header(f"If-Match: {rev}")
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionACLError(f'Delete user failed: {json.loads(response.read())}'),
header=response.header_items())
We will now write the add_index
and delete_index
methods, which are mainly concerned with creating indexes for the database.
def add_index(self, name, fields):
url = f"{self.database}/_index"
data = {"name": name, 'type': 'json', "index": {'fields': fields}}
req = urllib.request.Request(url,
data=json.dumps(data).encode('utf8'),
method='POST')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 201:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionError(f'Index creation error: {json.loads(response.read())}'),
header=response.header_items())
def delete_index(self, ddoc, name):
url = f"{self.database}/_index/{ddoc}/json/{name}"
req = urllib.request.Request(url, method='DELETE')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionError(f'Index deletion error: {json.loads(response.read())}'),
header=response.header_items())
Finally, let’s add the database compact
method, which is useful after inserting a lot of data into the database to reduce the physical disk space.
def compact(self):
url = f"{self.database}/_compact"
req = urllib.request.Request(url, method='POST')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionError(f'Compaction error: {json.loads(response.read())}'),
header=response.header_items())
Batch class¶
Since we have already defined “bulk” operations in the insert_many
and in the update_many
in the Session
class,
we can define the get bulk through a Batch
class.
class Batch(nosqlapi.DocBatch):
"""CouchDB batch class; multiple get from session."""
def execute(self):
data = {"docs": self.batch}
url = f"{self.session.database}/_bulk_get"
req = urllib.request.Request(url, method='POST')
req.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(req)
if response.status_code == 200:
return Response(data=json.loads(response.read()),
code=response.status_code,
error=None,
header=response.header_items())
else:
return Response(data=None,
code=response.status_code,
error=noslapi.SessionError(f'Get multiple document error: {json.loads(response.read())}'),
header=response.header_items())
Selector class¶
Now instead, let’s define the last class that will represent the query shape for our CouchDB server, the Selector
class.
class Selector(nosqlapi.DocSelector):
"""CouchDB selector class; query representation."""
pass
Utils¶
The utils classes and functions they map objects that represent data on the CouchDB server. These types of objects are called ODMs.
Create a utils.py
module.
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# utils.py
"""Python utility library for document CouchDB server"""
from nosqlapi.docdb import Database, Document, Index
import core
import json
connect function¶
Let’s create a simple function that will create a Connection
object for us and return a Session
object.
We will call it connect()
.
def connect(host='localhost', port=5984, username=None, password=None, ssl=None, tls=None, cert=None,
database=None, ca_cert=None, ca_bundle=None):
conn = core.Connection(host='localhost', port=5984, username=None, password=None, ssl=None, tls=None, cert=None,
database=None, ca_cert=None, ca_bundle=None)
return conn.connect()
ODM classes¶
Now let’s define a DesignDocument
class, which will represent a design document in the CouchDB server.
class DesignDocument(Document):
"""Design document"""
def __init__(self, oid=None, views=None, updates=None, filters=None, validate_doc_update=None):
super().__init__(oid)
self._id = self['_id'] = f'_design/{self.id}'
self["language"] = "javascript"
self['views'] = {}
self['updates'] = {}
self['filters'] = {}
if views:
self['views'].update(views)
if updates:
self['updates'].update(updates)
if filters:
self['filters'].update(filters)
if validate_doc_update:
self['validate_doc_update'] = validate_doc_update
Now let’s define a PermissionDocument
class, which will represent a permission document in the CouchDB server.
class PermissionDocument(Document):
"""Permission document"""
def __init__(self, admins=None, members=None):
super().__init__()
self._id = None
del self['_id']
self["admins"] = {"names": [], "roles": []} if not admins else admins
self['members'] = {"names": [], "roles": []} if not members else members
Note
Now that we have defined some classes that represent documents, we can adapt our methods of the Session class around these ODM types.
If you want to see more examples, clone the official repository of nosqlapi
and find in the tests folder all the examples for each type of database.
$ git clone https://github.com/MatteoGuadrini/nosqlapi.git
$ cd nosqlapi
$ python -m unittest discover tests
$ ls -l tests