Source code for mango

"""Mango

A wrapper module that provides simple basic MongoDb operations.

date: 2016-12-19
license: MIT

changes:

2017-09-08
    - Changed pattern: modular to object oriented which make available to
      connect multiple databases
    - Version bumped: 0.2

2016-12-19
    - web2py DAL support
    - development started
    - stable release
    - Version bumped: 0.1.7
"""


import collections
import random
import pymongo
from bson.objectid import ObjectId


__version__ = "0.2"
IS_W2P_IMPORT = True


try:
    from gluon.contrib.appconfig import AppConfig
except:
    IS_W2P_IMPORT = False


[docs]def object_id(arg=None): """Convert input to a valid Mongodb ObjectId instance. object_id("<random>") -> ObjectId (not unique) instance REF: web2py's dal Args: arg ([type], optional): Defaults to None. [description] Raises: ValueError: Invalid ObjectID argument string ValueError: Invalid ObjectID. Requires an integer or base 16 value TypeError: object_id argument must be of type ObjectID or an objectid representable integer. Returns: ObjectId: Mongodb ObjectId """ if not arg: arg = 0 if isinstance(arg, basestring): # we assume an integer as default input rawhex = len(arg.replace("0x", "").replace("L", "")) == 24 if arg.isdigit() and (not rawhex): arg = int(arg) elif arg == "<random>": arg = int("0x%s" % "".join([ random.choice("0123456789abcdef") for x in range(24)]), 0) elif arg.isalnum(): if not arg.startswith("0x"): arg = "0x%s" % arg try: arg = int(arg, 0) except ValueError as e: raise ValueError( "invalid objectid argument string: %s" % e) else: raise ValueError("Invalid objectid argument string. " + "Requires an integer or base 16 value") elif isinstance(arg, ObjectId): return arg elif not isinstance(arg, (int, long)): raise TypeError( "object_id argument must be of type ObjectId or an objectid " + "representable integer (type %s)" % type(arg)) hexvalue = hex(arg)[2:].rstrip('L').zfill(24) return ObjectId(hexvalue)
[docs]def w2p_id(arg=None, to_str=True): """Convert ObjectId to web2py ID. REF: web2py's dal Args: arg ([type], optional): Defaults to None. [description] to_str (bool, optional): Defaults to True. [description] Raises: TypeError: [description] Returns: [type]: [description] """ if not isinstance(arg, ObjectId): raise TypeError('w2p_id argument must be ObjectId') converted = long(str(arg), 16) if to_str: return str(converted) return converted
[docs]def to_w2p_id(records, to_web2py_id=False, to_str=True): """Convert ObjectId of records to web2py compitible ID. Args: records ([type]): [description] to_web2py_id (bool, optional): Defaults to False. [description] to_str (bool, optional): Defaults to True. [description] Returns: [type]: [description] """ mongodb_id_field = '_id' def converter(row): if mongodb_id_field in row: value = row.get(mongodb_id_field) row['id'] = w2p_id(value, to_str=to_str) row.pop(mongodb_id_field) return row if to_web2py_id: if isinstance(records, dict): return converter(records) elif isinstance(records, collections.Iterable): return map(converter, records) return records
[docs]def encoding_handler(value, encoding='utf8'): """To handle string encoding. Returns: [type]: [description] """ if isinstance(value, str): return value.decode(encoding) elif isinstance(value, unicode): return value.encode(encoding) else: return value
[docs]class Mango(object): """PyMongo Wrapper: Mango """ _uri = None _client = None _db = None _db_name = None _connected = False _last_exception = None def __init__(self, uri=None): """Initialize MongoDb Connection using PyMongo. Args: uri ([type], optional): Defaults to None. [description] """ self._uri = uri if IS_W2P_IMPORT and not uri: appconfig = AppConfig() self._uri = appconfig.get('db.uri') if self._uri: self._client = pymongo.MongoClient(self._uri) self._db = self._client.get_default_database() self._db_name = self._db.name def connect(self): try: self._client.server_info() self._connected = True except pymongo.errors.ServerSelectionTimeoutError as sste: self._last_exception = sste self._connected = False return self._connected def disconnect(self): if self._connected: self._client.close()
[docs] def get_table(self, table_name): """Get the table object by table_name. Args: table_name ([type]): [description] Returns: [type]: [description] """ return self._db.get_collection(table_name)
[docs] def count(self, table_name, cond=None, **kwargs): """MongoDb count wrapper function. Args: table_name ([type]): [description] cond ([type], optional): Defaults to None. [description] Returns: [type]: [description] """ _table = self.get_table(table_name) return _table.count(cond, **kwargs)
[docs] def select(self, table_name, cond=None, is_many=False, to_web2py_id=False, **kwargs): """MongoDb find wrapper function. Args: table_name ([type]): [description] cond ([type], optional): Defaults to None. [description] is_many (bool, optional): Defaults to False. [description] to_web2py_id (bool, optional): Defaults to False. [description] Returns: [type]: [description] """ _table = self.get_table(table_name) if not is_many: return to_w2p_id( _table.find_one(cond, **kwargs), to_web2py_id) else: return to_w2p_id( _table.find(cond, **kwargs), to_web2py_id)
[docs] def delete(self, table_name, cond=None, is_many=False, **kwargs): """MongoDb delete wrapper function. Args: table_name ([type]): [description] cond ([type], optional): Defaults to None. [description] is_many (bool, optional): Defaults to False. [description] Returns: [type]: [description] """ _table = self.get_table(table_name) if not is_many: return _table.delete_one( cond, **kwargs) else: return _table.delete_many( cond, **kwargs)
[docs] def update(self, table_name, cond=None, value=None, is_many=False, operator="$set", **kwargs): """MongoDb update wrapper function. Args: table_name ([type]): [description] cond ([type], optional): Defaults to None. [description] value ([type], optional): Defaults to None. If operator is provided in `value`, `operator` argument will be ignored. is_many (bool, optional): Defaults to False. [description] operator (str, optional): Defaults to "$set". [description] Returns: [type]: [description] """ _table = self.get_table(table_name) _update = value _has_operators = any([k.startswith("$") for k in value.keys()]) if cond and value: if not _has_operators: _update = { operator: value } if not is_many: return _table.update_one( cond, _update, **kwargs) else: return _table.update_many( cond, _update, **kwargs) else: return None
[docs] def insert(self, table_name, value=None, is_many=False, **kwargs): """MongoDb insert wrapper function. Args: table_name ([type]): [description] value ([type], optional): Defaults to None. [description] is_many (bool, optional): Defaults to False. [description] Returns: [type]: [description] """ _table = self.get_table(table_name) if not is_many: return _table.insert_one(value, **kwargs) else: return _table.insert_many(value, **kwargs)