149 lines
5.5 KiB
Python
149 lines
5.5 KiB
Python
|
from ..helpers.classes import FakeCursor, SQLALL
|
||
|
from ..helpers.methods import uuid2int
|
||
|
from ..objects import Query, Field
|
||
|
from .base import NoSQLAdapter, SQLAdapter
|
||
|
from . import adapters
|
||
|
|
||
|
|
||
|
@adapters.register_for('couchdb')
|
||
|
class CouchDB(NoSQLAdapter):
|
||
|
dbengine = 'couchdb'
|
||
|
drivers = ('couchdb',)
|
||
|
|
||
|
uploads_in_blob = True
|
||
|
|
||
|
def _initialize_(self, do_connect):
|
||
|
super(CouchDB, self)._initialize_(do_connect)
|
||
|
self.ruri = 'http://' + self.uri[10:]
|
||
|
self.db_codec = 'UTF-8'
|
||
|
|
||
|
def connector(self):
|
||
|
conn = self.driver.Server(self.ruri, **self.driver_args)
|
||
|
conn.cursor = lambda: FakeCursor()
|
||
|
conn.close = lambda: None
|
||
|
conn.commit = lambda: None
|
||
|
return conn
|
||
|
|
||
|
def create_table(self, table, migrate=True, fake_migrate=False,
|
||
|
polymodel=None):
|
||
|
if migrate:
|
||
|
try:
|
||
|
self.connection.create(table._tablename)
|
||
|
except:
|
||
|
pass
|
||
|
super(CouchDB, self).create_table(
|
||
|
table, migrate, fake_migrate, polymodel)
|
||
|
|
||
|
def _expand(self, expression, field_type=None, query_env={}):
|
||
|
if isinstance(expression, Field):
|
||
|
if expression.type == 'id':
|
||
|
return "%s._id" % expression.tablename
|
||
|
return SQLAdapter._expand(self, expression, field_type,
|
||
|
query_env=query_env)
|
||
|
|
||
|
def insert(self, table, fields):
|
||
|
rid = uuid2int(self.db.uuid())
|
||
|
ctable = self.connection[table._tablename]
|
||
|
values = dict((k.name, self.represent(v, k.type)) for k, v in fields)
|
||
|
values['_id'] = str(rid)
|
||
|
ctable.save(values)
|
||
|
return rid
|
||
|
|
||
|
@staticmethod
|
||
|
def _make_id_field(field_name):
|
||
|
return field_name == 'id' and '_id' or field_name
|
||
|
|
||
|
def _select(self, query, fields, left=False, join=False, distinct=False,
|
||
|
orderby=False, groupby=False, having=False, limitby=False,
|
||
|
orderby_on_limitby=True, for_update=False, outer_scoped=[],
|
||
|
required=None, cache=None, cacheable=None, processor=None):
|
||
|
if not isinstance(query, Query):
|
||
|
raise SyntaxError("Not Supported")
|
||
|
|
||
|
new_fields = []
|
||
|
for item in fields:
|
||
|
if isinstance(item, SQLALL):
|
||
|
new_fields += item._table
|
||
|
else:
|
||
|
new_fields.append(item)
|
||
|
|
||
|
fields = new_fields
|
||
|
tablename = self.get_table(query)._tablename
|
||
|
fieldnames = [f.name for f in (fields or self.db[tablename])]
|
||
|
colnames = [
|
||
|
'%s.%s' % (tablename, fieldname) for fieldname in fieldnames]
|
||
|
fields = ','.join(
|
||
|
['%s.%s' % (tablename, self._make_id_field(f)) for f in fieldnames]
|
||
|
)
|
||
|
fn = '(function(%(t)s){if(%(query)s)emit(%(order)s,[%(fields)s]);})' \
|
||
|
% dict(
|
||
|
t=tablename, query=self.expand(query),
|
||
|
order='%s._id' % tablename, fields=fields)
|
||
|
return fn, colnames
|
||
|
|
||
|
def select(self, query, fields, attributes):
|
||
|
fn, colnames = self._select(query, fields, attributes)
|
||
|
tablename = colnames[0].split('.')[0]
|
||
|
ctable = self.connection[tablename]
|
||
|
rows = [cols['value'] for cols in ctable.query(fn)]
|
||
|
processor = attributes.get('processor', self.parse)
|
||
|
return processor(rows, fields, colnames, False)
|
||
|
|
||
|
def update(self, table, query, fields):
|
||
|
from ..drivers import couchdb
|
||
|
if not isinstance(query, Query):
|
||
|
raise SyntaxError("Not Supported")
|
||
|
if query.first.type == 'id' and query.op == self.dialect.eq:
|
||
|
rid = query.second
|
||
|
tablename = query.first.tablename
|
||
|
ctable = self.connection[tablename]
|
||
|
try:
|
||
|
doc = ctable[str(rid)]
|
||
|
for key, value in fields:
|
||
|
doc[key.name] = self.represent(
|
||
|
value, self.db[tablename][key.name].type)
|
||
|
ctable.save(doc)
|
||
|
return 1
|
||
|
except couchdb.http.ResourceNotFound:
|
||
|
return 0
|
||
|
tablename = self.get_table(query)._tablename
|
||
|
rows = self.select(query, [self.db[tablename]._id], {})
|
||
|
ctable = self.connection[tablename]
|
||
|
table = self.db[tablename]
|
||
|
for row in rows:
|
||
|
doc = ctable[str(row.id)]
|
||
|
for key, value in fields:
|
||
|
doc[key.name] = self.represent(value, table[key.name].type)
|
||
|
ctable.save(doc)
|
||
|
return len(rows)
|
||
|
|
||
|
def count(self, query, distinct=None):
|
||
|
if distinct:
|
||
|
raise RuntimeError("COUNT DISTINCT not supported")
|
||
|
if not isinstance(query, Query):
|
||
|
raise SyntaxError("Not Supported")
|
||
|
tablename = self.get_table(query)._tablename
|
||
|
rows = self.select(query, [self.db[tablename]._id], {})
|
||
|
return len(rows)
|
||
|
|
||
|
def delete(self, table, query):
|
||
|
from ..drivers import couchdb
|
||
|
if not isinstance(query, Query):
|
||
|
raise SyntaxError("Not Supported")
|
||
|
if query.first.type == 'id' and query.op == self.eq:
|
||
|
rid = query.second
|
||
|
tablename = query.first.tablename
|
||
|
assert(tablename == query.first.tablename)
|
||
|
ctable = self.connection[tablename]
|
||
|
try:
|
||
|
del ctable[str(rid)]
|
||
|
return 1
|
||
|
except couchdb.http.ResourceNotFound:
|
||
|
return 0
|
||
|
tablename = self.get_table(query)._tablename
|
||
|
rows = self.select(query, [self.db[tablename]._id], {})
|
||
|
ctable = self.connection[tablename]
|
||
|
for row in rows:
|
||
|
del ctable[str(row.id)]
|
||
|
return len(rows)
|