123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- # -*- coding: utf-8 -*-
- import pymongo
- from pymongo import MongoClient
- from pymongo.errors import BulkWriteError
- import urllib
-
- import lodel.datasource.mongodb.utils as utils
-
- from lodel.datasource.generic.datasource import GenericDataSource
-
-
- class MongoDbDataSourceError(Exception):
- pass
-
-
- class MongoDbDataSource(GenericDataSource):
-
- MANDATORY_CONNECTION_ARGS = ('host', 'port', 'login', 'password', 'dbname')
-
- ## @brief Instanciates a Database object given a connection name
- # @param connection_name str
- def __init__(self, connection_name='default'):
- connection_args = self._get_connection_args(connection_name)
- login, password, host, port, dbname = MongoDbDataSource._check_connection_args(connection_args)
-
- # Creating of the connection
- connection_string = 'mongodb://%s:%s@%s:%s' % (login, password, host, port)
- self.connection = MongoClient(connection_string)
- # Getting the database
- self.database = self.connection[dbname]
-
- ## @brief Gets the settings given a connection name
- # @param connection_name str
- # @return dict
- # @TODO Change the return value using the Lodel 2 settings module
- def _get_connection_args(self, connection_name):
- return {
- 'host': 'localhost',
- 'port': 27017,
- 'login': 'login', # TODO modifier la valeur
- 'password': 'password', # TODO modifier la valeur
- 'dbname': 'lodel'
- }
-
- ## @brief checks if the connection args are valid and complete
- # @param connection_args dict
- # @return bool
- # @todo checks on the argument types can be added here
- @classmethod
- def _check_connection_args(cls, connection_args):
- errors = []
- for connection_arg in cls.MANDATORY_CONNECTION_ARGS:
- if connection_arg not in connection_args:
- errors.append("Datasource connection error : %s parameter is missing." % connection_arg)
- if len(errors) > 0 :
- raise MongoDbDataSourceError("\r\n-".join(errors))
-
- return (connection_args['login'], urllib.quote_plus(connection_args['password']), connection_args['host'],
- connection_args['port'], connection_args['dbname'])
-
- ## @brief returns a selection of documents from the datasource
- # @param target_cls Emclass
- # @param field_list list
- # @param filters list : List of filters
- # @param rel_filters list : List of relational filters
- # @param order list : List of column to order. ex: order = [('title', 'ASC'),]
- # @param group list : List of tupple representing the column to group together. ex: group = [('title', 'ASC'),]
- # @param limit int : Number of records to be returned
- # @param offset int: used with limit to choose the start record
- # @param instanciate bool : If true, the records are returned as instances, else they are returned as dict
- # @return list
- # @todo Implement the grouping
- # @todo Implement the relations
- def select(self, target_cls, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
- instanciate=True):
- collection_name = utils.object_collection_name(target_cls.__class__)
- collection = self.database[collection_name]
- query_filters = utils.parse_query_filters(filters)
- query_result_ordering = utils.parse_query_order(order) if order is not None else None
- results_field_list = None if len(field_list) == 0 else field_list
- limit = limit if limit is not None else 0
- cursor = collection.find(
- filter=query_filters,
- projection=results_field_list,
- skip=offset,
- limit=limit,
- sort=query_result_ordering
- )
- results = list()
- for document in cursor:
- results.append(document)
-
- return results
-
- ## @brief Deletes one record defined by its uid
- # @param target_cls Emclass : class of the record to delete
- # @param uid dict|list : a dictionary of fields and values composing the unique identifier of the record or a list of several dictionaries
- # @return int : number of deleted records
- # @TODO check the content of the result.raw_result property depending on the informations to return
- # @TODO Implement the error management
- def delete(self, target_cls, uid):
- if isinstance(uid, dict):
- uid = [uid]
- collection_name = utils.object_collection_name(target_cls.__class__)
- collection = self.database[collection_name]
- result = collection.delete_many(uid)
- return result.deleted_count
-
- ## @brief updates one or a list of records
- # @param target_cls Emclass : class of the object to insert
- # @param uids list : list of uids to update
- # @param datas dict : datas to update (new values)
- # @return int : Number of updated records
- # @todo check if the values need to be parsed
- def update(self, target_cls, uids, **datas):
- if not isinstance(uids, list):
- uids = [uids]
- collection_name = utils.object_collection_name(target_cls.__class__)
- collection = self.database[collection_name]
- results = collection.update_many({'uid': {'$in': uids}}, datas)
- return results.modified_count()
-
- ## @brief Inserts a record in a given collection
- # @param target_cls Emclass : class of the object to insert
- # @param datas dict : datas to insert
- # @return bool
- # @TODO Implement the error management
- def insert(self, target_cls, **datas):
- collection_name = utils.object_collection_name(target_cls.__class__)
- collection = self.database[collection_name]
- result = collection.insert_one(datas)
- return len(result.inserted_id)
-
- ## @brief Inserts a list of records in a given collection
- # @param target_cls Emclass : class of the objects inserted
- # @param datas_list
- # @return list : list of the inserted records' ids
- # @TODO Implement the error management
- def insert_multi(self, target_cls, datas_list):
- collection_name = utils.object_collection_name(target_cls.__class__)
- collection = self.database[collection_name]
- result = collection.insert_many(datas_list)
- return len(result.inserted_ids)
|