mirror of
https://github.com/yweber/lodel2.git
synced 2026-01-14 10:42:14 +01:00
Added the "group by" option in the select method of the mongodb datasource and started the implementation of the migration handler
This commit is contained in:
parent
d30f3e189f
commit
825dea770b
4 changed files with 141 additions and 10 deletions
46
lodel/datasource/generic/migrationhandler.py
Normal file
46
lodel/datasource/generic/migrationhandler.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
## @package lodel.datasource.migrationhandler.generic
|
||||||
|
# @brief A generic migration handler
|
||||||
|
#
|
||||||
|
# According to it, every moditification is possible
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
## Manage model changes
|
||||||
|
class GenericMigrationHandler(object):
|
||||||
|
|
||||||
|
def __init__(self, debug=False):
|
||||||
|
self.debug = debug
|
||||||
|
|
||||||
|
## @brief Records a change in the EditorialModel and indicates whether or not it is possible to make it
|
||||||
|
# @note The states (initial_state and new_state) contains only fields that changes
|
||||||
|
def register_change(self, em, uid, initial_state, new_state):
|
||||||
|
if self.debug:
|
||||||
|
print("\n##############")
|
||||||
|
print("GenericMigrationHandler debug. Changes for component with uid %s :" % uid)
|
||||||
|
if initial_state is None:
|
||||||
|
print("Component creation (uid = %s): \n\t" % uid, new_state)
|
||||||
|
elif new_state is None:
|
||||||
|
print("Component deletion (uid = %s): \n\t" % uid, initial_state)
|
||||||
|
else:
|
||||||
|
field_list = set(initial_state.keys()).union(set(new_state.keys()))
|
||||||
|
for field_name in field_list:
|
||||||
|
str_chg = "\t%s " % field_name
|
||||||
|
if field_name in initial_state:
|
||||||
|
str_chg += "'" + str(initial_state[field_name]) + "'"
|
||||||
|
else:
|
||||||
|
str_chg += " creating "
|
||||||
|
str_chg += " => "
|
||||||
|
if field_name in new_state:
|
||||||
|
str_chg += "'" + str(new_state[field_name]) + "'"
|
||||||
|
else:
|
||||||
|
str_chg += " deletion "
|
||||||
|
print(str_chg)
|
||||||
|
|
||||||
|
print("##############\n")
|
||||||
|
|
||||||
|
## @brief Not usefull for the moment
|
||||||
|
def register_model_state(self, em, state_hash):
|
||||||
|
if self.debug:
|
||||||
|
print("New EditorialModel state registered : '%s'" % state_hash)
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import bson
|
||||||
|
from bson.son import SON
|
||||||
|
from collections import OrderedDict
|
||||||
import pymongo
|
import pymongo
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
from pymongo.errors import BulkWriteError
|
from pymongo.errors import BulkWriteError
|
||||||
|
|
@ -64,12 +68,11 @@ class MongoDbDataSource(GenericDataSource):
|
||||||
# @param filters list : List of filters
|
# @param filters list : List of filters
|
||||||
# @param rel_filters list : List of relational filters
|
# @param rel_filters list : List of relational filters
|
||||||
# @param order list : List of column to order. ex: order = [('title', 'ASC'),]
|
# @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 group list : List of tupple representing the column used as "group by" fields. ex: group = [('title', 'ASC'),]
|
||||||
# @param limit int : Number of records to be returned
|
# @param limit int : Number of records to be returned
|
||||||
# @param offset int: used with limit to choose the start record
|
# @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
|
# @param instanciate bool : If true, the records are returned as instances, else they are returned as dict
|
||||||
# @return list
|
# @return list
|
||||||
# @todo Implement the grouping
|
|
||||||
# @todo Implement the relations
|
# @todo Implement the relations
|
||||||
def select(self, target_cls, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
|
def select(self, target_cls, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
|
||||||
instanciate=True):
|
instanciate=True):
|
||||||
|
|
@ -79,6 +82,8 @@ class MongoDbDataSource(GenericDataSource):
|
||||||
query_result_ordering = utils.parse_query_order(order) if order is not None else None
|
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
|
results_field_list = None if len(field_list) == 0 else field_list
|
||||||
limit = limit if limit is not None else 0
|
limit = limit if limit is not None else 0
|
||||||
|
|
||||||
|
if group is None:
|
||||||
cursor = collection.find(
|
cursor = collection.find(
|
||||||
filter=query_filters,
|
filter=query_filters,
|
||||||
projection=results_field_list,
|
projection=results_field_list,
|
||||||
|
|
@ -86,6 +91,32 @@ class MongoDbDataSource(GenericDataSource):
|
||||||
limit=limit,
|
limit=limit,
|
||||||
sort=query_result_ordering
|
sort=query_result_ordering
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
pipeline = list()
|
||||||
|
unwinding_list = list()
|
||||||
|
grouping_dict = OrderedDict()
|
||||||
|
sorting_list = list()
|
||||||
|
for group_param in group:
|
||||||
|
field_name = group_param[0]
|
||||||
|
field_sort_option = group_param[1]
|
||||||
|
sort_option = utils.MONGODB_SORT_OPERATORS_MAP[field_sort_option]
|
||||||
|
unwinding_list.append({'$unwind': '$%s' % field_name})
|
||||||
|
grouping_dict[field_name] = '$%s' % field_name
|
||||||
|
sorting_list.append((field_name, sort_option))
|
||||||
|
|
||||||
|
sorting_list.extends(query_result_ordering)
|
||||||
|
|
||||||
|
pipeline.append({'$match': query_filters})
|
||||||
|
if results_field_list is not None:
|
||||||
|
pipeline.append({'$project': SON([{field_name: 1} for field_name in field_list])})
|
||||||
|
pipeline.extend(unwinding_list)
|
||||||
|
pipeline.append({'$group': grouping_dict})
|
||||||
|
pipeline.extend({'$sort': SON(sorting_list)})
|
||||||
|
if offset > 0:
|
||||||
|
pipeline.append({'$skip': offset})
|
||||||
|
if limit is not None:
|
||||||
|
pipeline.append({'$limit': limit})
|
||||||
|
|
||||||
results = list()
|
results = list()
|
||||||
for document in cursor:
|
for document in cursor:
|
||||||
results.append(document)
|
results.append(document)
|
||||||
|
|
|
||||||
|
|
@ -1 +1,51 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from lodel.datasource.generic.migrationhandler import GenericMigrationHandler
|
||||||
|
from lodel.datasource.mongodb.datasource import MongoDbDataSource
|
||||||
|
from lodel.editorial_model.components import EmClass, EmField
|
||||||
|
class MigrationHandlerChangeError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
## @brief Modifies a MongoDb database given editorial model changes
|
||||||
|
class MongoDbMigrationHandler(GenericMigrationHandler):
|
||||||
|
|
||||||
|
## @brief constructs a MongoDbMigrationHandler
|
||||||
|
# @param conn_args dict : a dictionary containing connection options
|
||||||
|
# @param **kwargs : extra arguments given to the connection methods
|
||||||
|
def __init__(self, conn_args=None, **kwargs):
|
||||||
|
if conn_args is None:
|
||||||
|
conn_args = {} # TODO : récupérer les options de connexion dans les settings
|
||||||
|
self.connection_name = conn_args['name']
|
||||||
|
# del conn_args['module']
|
||||||
|
|
||||||
|
self.db_conn = MongoDbDataSource(self.connection_name)
|
||||||
|
# TODO Réimplémenter la partie sur les settings
|
||||||
|
mh_settings = {}
|
||||||
|
self.dryrun = kwargs['dryrun'] if 'dryrun' in kwargs else mh_settings['dryrun']
|
||||||
|
self.foreign_keys = kwargs['foreign_keys'] if 'foreign_keys' in kwargs else mh_settings['foreign_keys']
|
||||||
|
self.drop_if_exists = kwargs['drop_if_exists'] if 'drop_if_exists' in kwargs else mh_settings['drop_if_exists']
|
||||||
|
self._create_default_collections(self.drop_if_exists)
|
||||||
|
|
||||||
|
## @brief Modify the database given an EM change
|
||||||
|
#
|
||||||
|
# @param em model : The EditorialModel.model object to provide the global context.
|
||||||
|
# @param uid str : The uid of the changed component.
|
||||||
|
# @param initial_state dict|None : dict with field name as key and field value as value. Represents the original state. None means it's a creation of a new component.
|
||||||
|
# @param new_state dict|None : dict with field name as key and field value as value. Represents the new state. None means it's a component deletion.
|
||||||
|
# @throw MigrationHandlerChangeError if the change was refused
|
||||||
|
def register_change(self, em, uid, initial_state, new_state):
|
||||||
|
if isinstance(em.classes(uid), EmClass):
|
||||||
|
if initial_state is None:
|
||||||
|
# EmClass creation
|
||||||
|
self.create_emclass_collection(em, uid)
|
||||||
|
elif new_state is None:
|
||||||
|
# EmClass deletion
|
||||||
|
self.delete_emclass_collection(em, uid)
|
||||||
|
elif isinstance(em.classes(uid), EmField):
|
||||||
|
emfield = em.
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _create_default_collections(self, drop_if_exist=False):
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ LODEL_SORT_OPERATORS_MAP = {
|
||||||
'DESC': pymongo.DESCENDING
|
'DESC': pymongo.DESCENDING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MONGODB_SORT_OPERATORS_MAP = {
|
||||||
|
'ASC': 1,
|
||||||
|
'DESC': -1
|
||||||
|
}
|
||||||
|
|
||||||
## @brief Returns a collection name given a Emclass name
|
## @brief Returns a collection name given a Emclass name
|
||||||
# @param class_name str : The class name
|
# @param class_name str : The class name
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue