Browse Source

Added the "group by" option in the select method of the mongodb datasource and started the implementation of the migration handler

Roland Haroutiounian 8 years ago
parent
commit
825dea770b

+ 46
- 0
lodel/datasource/generic/migrationhandler.py View File

@@ -0,0 +1,46 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+## @package lodel.datasource.migrationhandler.generic
4
+# @brief A generic migration handler
5
+#
6
+# According to it, every moditification is possible
7
+#
8
+
9
+
10
+## Manage model changes
11
+class GenericMigrationHandler(object):
12
+
13
+    def __init__(self, debug=False):
14
+        self.debug = debug
15
+
16
+    ## @brief Records a change in the EditorialModel and indicates whether or not it is possible to make it
17
+    # @note The states (initial_state and new_state) contains only fields that changes
18
+    def register_change(self, em, uid, initial_state, new_state):
19
+        if self.debug:
20
+            print("\n##############")
21
+            print("GenericMigrationHandler debug. Changes for component with uid %s :" % uid)
22
+            if initial_state is None:
23
+                print("Component creation (uid = %s): \n\t" % uid, new_state)
24
+            elif new_state is None:
25
+                print("Component deletion (uid = %s): \n\t" % uid, initial_state)
26
+            else:
27
+                field_list = set(initial_state.keys()).union(set(new_state.keys()))
28
+                for field_name in field_list:
29
+                    str_chg = "\t%s " % field_name
30
+                    if field_name in initial_state:
31
+                        str_chg += "'" + str(initial_state[field_name]) + "'"
32
+                    else:
33
+                        str_chg += " creating "
34
+                    str_chg += " => "
35
+                    if field_name in new_state:
36
+                        str_chg += "'" + str(new_state[field_name]) + "'"
37
+                    else:
38
+                        str_chg += " deletion "
39
+                    print(str_chg)
40
+
41
+            print("##############\n")
42
+
43
+    ## @brief Not usefull for the moment
44
+    def register_model_state(self, em, state_hash):
45
+        if self.debug:
46
+            print("New EditorialModel state registered : '%s'" % state_hash)

+ 40
- 9
lodel/datasource/mongodb/datasource.py View File

@@ -1,4 +1,8 @@
1 1
 # -*- coding: utf-8 -*-
2
+
3
+import bson
4
+from bson.son import SON
5
+from collections import OrderedDict
2 6
 import pymongo
3 7
 from pymongo import MongoClient
4 8
 from pymongo.errors import BulkWriteError
@@ -64,12 +68,11 @@ class MongoDbDataSource(GenericDataSource):
64 68
     # @param filters list : List of filters
65 69
     # @param rel_filters list : List of relational filters
66 70
     # @param order list : List of column to order. ex: order = [('title', 'ASC'),]
67
-    # @param group list : List of tupple representing the column to group together. ex: group = [('title', 'ASC'),]
71
+    # @param group list : List of tupple representing the column used as "group by" fields. ex: group = [('title', 'ASC'),]
68 72
     # @param limit int : Number of records to be returned
69 73
     # @param offset int: used with limit to choose the start record
70 74
     # @param instanciate bool : If true, the records are returned as instances, else they are returned as dict
71 75
     # @return list
72
-    # @todo Implement the grouping
73 76
     # @todo Implement the relations
74 77
     def select(self, target_cls, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
75 78
                instanciate=True):
@@ -79,13 +82,41 @@ class MongoDbDataSource(GenericDataSource):
79 82
         query_result_ordering = utils.parse_query_order(order) if order is not None else None
80 83
         results_field_list = None if len(field_list) == 0 else field_list
81 84
         limit = limit if limit is not None else 0
82
-        cursor = collection.find(
83
-            filter=query_filters,
84
-            projection=results_field_list,
85
-            skip=offset,
86
-            limit=limit,
87
-            sort=query_result_ordering
88
-        )
85
+
86
+        if group is None:
87
+            cursor = collection.find(
88
+                filter=query_filters,
89
+                projection=results_field_list,
90
+                skip=offset,
91
+                limit=limit,
92
+                sort=query_result_ordering
93
+            )
94
+        else:
95
+            pipeline = list()
96
+            unwinding_list = list()
97
+            grouping_dict = OrderedDict()
98
+            sorting_list = list()
99
+            for group_param in group:
100
+                field_name = group_param[0]
101
+                field_sort_option = group_param[1]
102
+                sort_option = utils.MONGODB_SORT_OPERATORS_MAP[field_sort_option]
103
+                unwinding_list.append({'$unwind': '$%s' % field_name})
104
+                grouping_dict[field_name] = '$%s' % field_name
105
+                sorting_list.append((field_name, sort_option))
106
+
107
+            sorting_list.extends(query_result_ordering)
108
+
109
+            pipeline.append({'$match': query_filters})
110
+            if results_field_list is not None:
111
+                pipeline.append({'$project': SON([{field_name: 1} for field_name in field_list])})
112
+            pipeline.extend(unwinding_list)
113
+            pipeline.append({'$group': grouping_dict})
114
+            pipeline.extend({'$sort': SON(sorting_list)})
115
+            if offset > 0:
116
+                pipeline.append({'$skip': offset})
117
+            if limit is not None:
118
+                pipeline.append({'$limit': limit})
119
+
89 120
         results = list()
90 121
         for document in cursor:
91 122
             results.append(document)

+ 51
- 1
lodel/datasource/mongodb/migration_handler.py View File

@@ -1 +1,51 @@
1
-# -*- coding: utf-8 -*-
1
+# -*- coding: utf-8 -*-
2
+
3
+from lodel.datasource.generic.migrationhandler import GenericMigrationHandler
4
+from lodel.datasource.mongodb.datasource import MongoDbDataSource
5
+from lodel.editorial_model.components import EmClass, EmField
6
+class MigrationHandlerChangeError(Exception):
7
+    pass
8
+
9
+## @brief Modifies a MongoDb database given editorial model changes
10
+class MongoDbMigrationHandler(GenericMigrationHandler):
11
+
12
+    ## @brief constructs a MongoDbMigrationHandler
13
+    # @param conn_args dict : a dictionary containing connection options
14
+    # @param **kwargs : extra arguments given to the connection methods
15
+    def __init__(self, conn_args=None, **kwargs):
16
+        if conn_args is None:
17
+            conn_args = {}  # TODO : récupérer les options de connexion dans les settings
18
+            self.connection_name = conn_args['name']
19
+            # del conn_args['module']
20
+
21
+        self.db_conn = MongoDbDataSource(self.connection_name)
22
+        # TODO Réimplémenter la partie sur les settings
23
+        mh_settings = {}
24
+        self.dryrun = kwargs['dryrun'] if 'dryrun' in kwargs else mh_settings['dryrun']
25
+        self.foreign_keys = kwargs['foreign_keys'] if 'foreign_keys' in kwargs else mh_settings['foreign_keys']
26
+        self.drop_if_exists = kwargs['drop_if_exists'] if 'drop_if_exists' in kwargs else mh_settings['drop_if_exists']
27
+        self._create_default_collections(self.drop_if_exists)
28
+
29
+    ## @brief Modify the database given an EM change
30
+    #
31
+    # @param em model : The EditorialModel.model object to provide the global context.
32
+    # @param uid str : The uid of the changed component.
33
+    # @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.
34
+    # @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.
35
+    # @throw MigrationHandlerChangeError if the change was refused
36
+    def register_change(self, em, uid, initial_state, new_state):
37
+        if isinstance(em.classes(uid), EmClass):
38
+            if initial_state is None:
39
+                # EmClass creation
40
+                self.create_emclass_collection(em, uid)
41
+            elif new_state is None:
42
+                # EmClass deletion
43
+                self.delete_emclass_collection(em, uid)
44
+        elif isinstance(em.classes(uid), EmField):
45
+            emfield = em.
46
+
47
+        pass
48
+
49
+
50
+    def _create_default_collections(self, drop_if_exist=False):
51
+        pass

+ 4
- 0
lodel/datasource/mongodb/utils.py View File

@@ -25,6 +25,10 @@ LODEL_SORT_OPERATORS_MAP = {
25 25
     'DESC': pymongo.DESCENDING
26 26
 }
27 27
 
28
+MONGODB_SORT_OPERATORS_MAP = {
29
+    'ASC': 1,
30
+    'DESC': -1
31
+}
28 32
 
29 33
 ## @brief Returns a collection name given a Emclass name
30 34
 # @param class_name str : The class name

Loading…
Cancel
Save