Skip to content

Commit

Permalink
Improved mapd client and compiler; Added initial documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
xmnlab committed Apr 7, 2018
1 parent b120529 commit 8c8df6d
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 59 deletions.
2 changes: 1 addition & 1 deletion ibis/__init__.py
Expand Up @@ -72,7 +72,7 @@
import ibis.bigquery.api as bigquery

with suppress(ImportError):
# pip install ibis-framework[bigquery]
# pip install ibis-framework[mapd]
import ibis.mapd.api as mapd

restart_ordering()
Expand Down
111 changes: 111 additions & 0 deletions ibis/mapd/README.rst
@@ -0,0 +1,111 @@
MapD IBIS backend
=================

In this document it would be explained the main aspects of `ibis` backend and
`MapD ibis` backend implementation.

Modules
-------

MapD backend has 5 modules, which 3 of them are the main modules:

- `api`
- `client`
- `compiler`

The `identifiers` and `operations` modules were created to organize `compiler`
method.

api
---

`api` module is the central key to access the backend. Ensure to include
the follow code into `ibi.__init__`:

.. code-block:: python
with suppress(ImportError):
# pip install ibis-framework[mapd]
import ibis.mapd.api as mapd
Basically, there is 3 function on `api` module>

- `compile`
- `connect`
- `verify`

`compile` method compiles a `ibis` expression to `SQL`:

.. code-block:: python
t = mapd_cli.table('flights_2008_10k')
proj = t['arrtime', 'arrdelay']
print(ibis.mapd.compile(proj))
`connect` method instantiates a `MapDClient` object that connect to the `MapD`
database specified:

.. code-block:: python
mapd_cli = ibis.mapd.connect(
host='localhost', user='mapd', password='HyperInteractive',
port=9091, dbname='mapd'
)
`verify` method checks if the `ibis` expression can be compiled.

.. code-block:: python
t = mapd_cli.table('flights_2008_10k')
proj = t['arrtime', 'arrdelay']
assert ibis.mapd.verify(proj) == True
client
------

`client` module has the main classes to handle connection to the `MapD`
database.

The main classes are:

- `MapDClient`
- `MapDDataType`

`MapDDataType` class is used to translate data type from `ibis` and to `ibis`.
Its main methods are:

- `parse`
- `to_ibis`
- `from_ibis`

`parse` method ... # @TODO

`to_ibis` method ... # @TODO

`from_ibis` method ... # @TODO

`MapDClient` class is used to connect to `MapD` database and manipulation data
expression. Its main methods are:

- __init__
- _build_ast
- _execute
- _fully_qualified_name
- _get_table_schema
- _table_expr_klass
- log
- close
- database
- current_database
- set_database
- exists_database
- list_databases
- exists_table
- list_tables
- get_schema
- version

References
----------

- ibis API: http://docs.ibis-project.org/api.html
108 changes: 64 additions & 44 deletions ibis/mapd/client.py
@@ -1,19 +1,11 @@
from ibis.compat import parse_version
from ibis.client import Database, Query, SQLClient
from ibis.client import Database, SQLClient
from ibis.mapd import compiler as comp
from ibis.util import log

import regex as re
import collections
import datetime

import six

import pandas as pd
import pymapd

from multipledispatch import Dispatcher

import ibis.common as com
import ibis.expr.types as ir
import ibis.expr.datatypes as dt
Expand All @@ -22,22 +14,25 @@
fully_qualified_re = re.compile(r"(.*)\.(?:`(.*)`|(.*))")

_mapd_dtypes = {
'BIGINT': dt.int64,
'BOOLEAN': dt.Boolean,
'CHAR': dt.string,
'DATE': dt.date,
'DECIMAL': dt.float,
'DOUBLE': dt.float,
'INT': dt.int32,
'FLOAT': dt.float,
'NULL': dt.Null,
'SMALLINT': dt.UInt8,
'UInt16': dt.UInt16,
'UInt32': dt.UInt32,
'UInt64': dt.UInt64,
'Int8': dt.Int8,
'Int16': dt.Int16,
'Int32': dt.Int32,
'Int64': dt.Int64,
'Float32': dt.Float32,
'FLOAT': dt.Float64,
'STR': dt.String,
'FixedString': dt.String,
'DATE': dt.Date,
'TIMESTAMP': dt.Timestamp
'NUMERIC': dt.float,
'REAL': dt.float,
'SMALLINT': dt.int8,
'STR': dt.string,
'TEXT': dt.string,
'TIME': dt.time,
'TIMESTAMP': dt.timestamp,
'VAR': dt.string,
}

_ibis_dtypes = {v: k for k, v in _mapd_dtypes.items()}
_ibis_dtypes[dt.String] = 'String'

Expand All @@ -63,7 +58,6 @@ def __repr__(self):

@classmethod
def parse(cls, spec):
# TODO(kszucs): spare parsing, depends on mapd-driver#22
if spec.startswith('Nullable'):
return cls(spec[9:-1], nullable=True)
else:
Expand All @@ -80,28 +74,11 @@ def from_ibis(cls, dtype, nullable=None):
return cls(typename, nullable=nullable)


@dt.dtype.register(MapDDataType)
def mapd_to_ibis_dtype(mapd_dtype):
return mapd_dtype.to_ibis()


class MapDQuery(Query):
"""
"""
pass


class MapDDatabase(Database):
pass


class MapDClient(SQLClient):
"""
"""
sync_query = MapDQuery
database_class = MapDDatabase
database_class = Database
dialect = comp.MapDDialect

def __init__(
Expand Down Expand Up @@ -180,14 +157,46 @@ def _execute(self, query):
return stmt_exec(query)

def database(self, name=None):
raise NotImplementedError()
"""Connect to a database called `name`.
Parameters
----------
name : str, optional
The name of the database to connect to. If ``None``, return
the database named ``self.current_database``.
Returns
-------
db : Database
An :class:`ibis.client.Database` instance.
Notes
-----
This creates a new connection if `name` is both not ``None`` and not
equal to the current database.
"""
if name == self.current_database or (
name is None and name != self.current_database
):
return self.database_class(self.current_database, self)
else:
client_class = type(self)
new_client = client_class(
uri=self.uri, user=self.user, password=self.password,
host=self.host, port=self.port, dbname=name,
protocol=self.protocol, execution_type=self.execution_type
)
return self.database_class(name, new_client)

@property
def current_database(self):
return self.dbname

def set_database(self, name):
raise NotImplementedError()
raise NotImplementedError(
'Cannot set database with MapD client. To use a different'
' database, use client.database({!r})'.format(name)
)

def exists_database(self, name):
raise NotImplementedError()
Expand Down Expand Up @@ -239,3 +248,14 @@ def get_schema(self, table_name, database=None):
@property
def version(self):
return parse_version(pymapd.__version__)


@dt.dtype.register(MapDDataType)
def mapd_to_ibis_dtype(mapd_dtype):
"""
Register MapD Data Types
:param mapd_dtype:
:return:
"""
return mapd_dtype.to_ibis()

0 comments on commit 8c8df6d

Please sign in to comment.