xdb

One database API to use them all


License
GPL-2.0+
Install
pip install xdb==0.0.0

Documentation

xdb

For now, the readme contains the specification for a "wildcard" DB called xdb.

The goal of xdb is to offer a single API for any database in Python.

Once a module has been written for a particular database, it is as easy as changing the 1) import statement and 2) server information to switch between backends. Let's say 2-5 lines of code, total.

**Disclaimer: This is my first attempt at writing a specification before actually writing code, so please bear with me. **

Collaboration: Note that I very much appreciate people collaborating on this specification (and of course, the actual writing of code) to make it better.

Has this been done before?

Well, I know that I have been writing some kind of translators for my sky project, see here for an example

Also, ZODB provides support for different mechanisms such as file, MySQL and Postgres.

Main motivation

  • No need to know the intricacies of each database, learn 1 API, use any database
  • Quick to mock using file as backend, then just switch to using a database.
  • Helpful for frameworks that want to support multiple databases
  • Could potentially really help with migrations between databases

Difficulties

  • Writing a single API that will be "good enough" to encapsulate ALL ("pragmatically" all) other APIs
  • async (will have low first priority)

Main objects

Mostly pseudo code here.

An Item class is named Item because it could be a Document (Document based type) or a Record (SQL type). Item seems neutral.

class Item():
    """ Not sure yet what functionality """
    pass

Database is an object that allows typical CRUD.

class Database(object):
    def __init__(self, server_information):
        pass

    def create_or_update(item):
        return "if not existing, create, otherwise update, and return status code"

    def create_or_update_bulk(items):
        return status_codes_for_each_item

    def get_item_bulk(conditions):
        return lots_of_items

    def get_item(conditions):
        """ Get item only under specific conditions
        if item is newest_item or item.id == X or etc.
        """
        pass

    def delete_item_if_exists(conditions):
        pass

Modules:

There would be one base to inherit from, and then anyone could write a compatible binding, stored in a module. I'm thinking Cloudant, ElasticSearch, File based, ZODB and many more.

Core

from xdb.base import Item
from xdb.base import Database

Cloudant

from xdb.cloudant import XItem
from xdb.cloudant import XDatabase

File based

from xdb.file import XItem
from xdb.file import XDatabase

Here the prepended X to the object shows that the prefix is replaceable; just like the wildcard "*". You could read it as CloudantDatabase, FileDatabase etc.

Importing

End goal would be to just change a single import wherever database stuff is used. Plus, the "server_information.py", which would contain information required for the specific database (see section below).

Import:

import xdb.file as db
import xdb.cloudant as db

So you can use in your file :

db.XDatabase
db.XItem

and still do not have to worry about the underlying APIs.

Server information

In your project, you'd define a server_information.py, depending on what backend you use. It contains the information neccessary to make a connection to an XDatabase object

def provide_server_information():
    """ For file based it would look like this """
    # Database is on toplevel path (here /local/path/db1/)
    # Table/ItemType is on sublevel path (here /local/path/db1/table1)
    # Items are files (here /local/path/db1/table1/record1.xdb)
    return {'path': '/local/path/'}

def provide_server_information():
    """ For cloudant it would look like this """
    from cloudant import account

    # or use token instead of username/password
    account = cloudant.Account(USERNAME)
    account.login(USERNAME, PASSWORD)

    return account

def provide_server_information():
    """ For elasticsearch it would look like this """
    import elasticsearch

    info = [{'host': 'localhost', 'port': 9200}]

    return elasticsearch.Elasticsearch(info)

How you'd use it

Considering you'd have a script where you get docs, update, filter, sort, juggle with them, all you have to do is change the toplevel import and change provide_server_information with one of the others:

/path/to/testing_mydb.py

import xdb.cloudant as db
from server_information import provide_server_information

mydb = db.XDatabase(provide_server_information())
items = mydb.get_bulk_items()

/path/to/server_information.py

from cloudant import account

def provide_server_information():
    account = cloudant.Account(USERNAME)
    account.login(USERNAME, PASSWORD)
    return account

Getting a superset of all functionality

This section tries to find out the best superset of functionality such that it can be used for all databases

API Database Topic Item Extras
File Path-0 Path-1 Path-1-File
ElasticSearch Database doc_type Document Searching/plugins
Cloudant Database Document Views, async
ZODB Database keys objects Transactional
SQL Database Table Record