Browse Source

Begin abstract LeObject child query factorisation in mongodb Datasource

Yann Weber 8 years ago
parent
commit
2668770369
2 changed files with 117 additions and 65 deletions
  1. 1
    1
      lodel/leapi/query.py
  2. 116
    64
      plugins/mongodb_datasource/datasource.py

+ 1
- 1
lodel/leapi/query.py View File

682
             target = self._target_class,
682
             target = self._target_class,
683
             field_list = list(self._field_list),
683
             field_list = list(self._field_list),
684
             filters = self._query_filter[0],
684
             filters = self._query_filter[0],
685
-            rel_filters = self._query_filter[1],
685
+            relational_filters = self._query_filter[1],
686
             order = self._order,
686
             order = self._order,
687
             group = self._group,
687
             group = self._group,
688
             limit = self._limit,
688
             limit = self._limit,

+ 116
- 64
plugins/mongodb_datasource/datasource.py View File

2
 
2
 
3
 import re
3
 import re
4
 import warnings
4
 import warnings
5
+import copy
6
+import operator
5
 from bson.son import SON
7
 from bson.son import SON
6
 from collections import OrderedDict
8
 from collections import OrderedDict
7
 import pymongo
9
 import pymongo
8
 from pymongo.errors import BulkWriteError
10
 from pymongo.errors import BulkWriteError
9
 
11
 
10
 from lodel import logger
12
 from lodel import logger
13
+from lodel.leapi.leobject import CLASS_ID_FIELDNAME
11
 
14
 
12
 from . import utils
15
 from . import utils
13
 from .utils import object_collection_name,\
16
 from .utils import object_collection_name,\
75
         target = emcomp.uid_source()
78
         target = emcomp.uid_source()
76
         tuid = target._uid[0] # Multiple UID broken here
79
         tuid = target._uid[0] # Multiple UID broken here
77
         results = self.select(
80
         results = self.select(
78
-            target, [tuid], [], order=[(tuid, 'DESC')], limit = 1)
81
+            target, field_list = [tuid], filters = [], 
82
+            order=[(tuid, 'DESC')], limit = 1)
79
         if len(results) == 0: 
83
         if len(results) == 0: 
80
             return 1
84
             return 1
81
         return results[0][tuid]+1
85
         return results[0][tuid]+1
86
     #@param filters list : List of filters
90
     #@param filters list : List of filters
87
     #@param rel_filters list : List of relational filters
91
     #@param rel_filters list : List of relational filters
88
     #@param order list : List of column to order. ex: order = [('title', 'ASC'),]
92
     #@param order list : List of column to order. ex: order = [('title', 'ASC'),]
89
-    #@param group list : List of tupple representing the column used as "group by" fields. ex: group = [('title', 'ASC'),]
93
+    #@param group list : List of tupple representing the column used as 
94
+    #"group by" fields. ex: group = [('title', 'ASC'),]
90
     #@param limit int : Number of records to be returned
95
     #@param limit int : Number of records to be returned
91
     #@param offset int: used with limit to choose the start record
96
     #@param offset int: used with limit to choose the start record
92
-    #@param instanciate bool : If true, the records are returned as instances, else they are returned as dict
93
     #@return list
97
     #@return list
94
-    #@todo Implement the relations
95
-    def select(self, target, field_list, filters = None, rel_filters=None, order=None, group=None, limit=None,
96
-               offset=0):
97
-        results = list()
98
+    #@todo Implement group for abstract LeObject childs
99
+    def select(self, target, field_list, filters = None,
100
+        relational_filters=None, order=None, group=None, limit=None, offset=0):
98
         if target.is_abstract():
101
         if target.is_abstract():
99
-            target_childs = target.child_classes()
100
-            for target_child in target_childs:
101
-                results += self.select(
102
-                    target=target_child, field_list=field_list,
103
-                    filters=filters, rel_filters=rel_filters, order=order,
104
-                    group=group, limit=limit, offset=offset)
102
+            #Reccursiv calls for abstract LeObject child
103
+            results =  self.__act_on_abstract(target, filters,
104
+                relational_filters, self.select, field_list = field_list,
105
+                order = order, group = group, limit = limit)
106
+            sort_itemgetter_args = list()
107
+            sort_dir = None
108
+            for fname, csort_dir in order:
109
+                sort_itemgetter_args.append(fname)
110
+                if sort_dir is None:
111
+                    sort_dir = csort_dir
112
+                elif sort_dir != csort_dir:
113
+                    raise NotImplementedError("Multiple direction for ordering\
114
+ is not implemented yet")
115
+            results = sorted(
116
+                results, key=operator.itemgetter(*sort_itemgetter_args),
117
+                reverse=False if sort_dir == 'ASC' else True)
118
+            if limit is not None:
119
+                results = results[offset:offset+limit]
120
+            return results
121
+                
122
+                
123
+        # Default behavior
124
+        filters = [] if filters is None else filters
125
+        relational_filters = [] if relational_filters is None else relational_filters
126
+
127
+        collection_name = object_collection_name(target)
128
+        collection = self.database[collection_name]
129
+
130
+        query_filters = self.__process_filters(target, filters, relational_filters)
131
+        query_result_ordering = None
132
+        if order is not None:
133
+            query_result_ordering = utils.parse_query_order(order)
134
+        results_field_list = None if len(field_list) == 0 else field_list
135
+        limit = limit if limit is not None else 0
136
+
137
+        if group is None:
138
+            cursor = collection.find(
139
+                filter=query_filters, projection=results_field_list,
140
+                skip=offset, limit=limit, sort=query_result_ordering)
105
         else:
141
         else:
106
-            # Default arg init
107
-            filters = [] if filters is None else filters
108
-            rel_filters = [] if rel_filters is None else rel_filters
109
-
110
-            collection_name = object_collection_name(target)
111
-            collection = self.database[collection_name]
112
-
113
-            query_filters = self.__process_filters(target, filters, rel_filters)
114
-            query_result_ordering = None
115
-            if order is not None:
116
-                query_result_ordering = utils.parse_query_order(order)
117
-            results_field_list = None if len(field_list) == 0 else field_list
118
-            limit = limit if limit is not None else 0
119
-
120
-            if group is None:
121
-                cursor = collection.find(
122
-                    filter=query_filters, projection=results_field_list,
123
-                    skip=offset, limit=limit, sort=query_result_ordering)
124
-            else:
125
-                pipeline = list()
126
-                unwinding_list = list()
127
-                grouping_dict = OrderedDict()
128
-                sorting_list = list()
129
-                for group_param in group:
130
-                    field_name = group_param[0]
131
-                    field_sort_option = group_param[1]
132
-                    sort_option = MONGODB_SORT_OPERATORS_MAP[field_sort_option]
133
-                    unwinding_list.append({'$unwind': '$%s' % field_name})
134
-                    grouping_dict[field_name] = '$%s' % field_name
135
-                    sorting_list.append((field_name, sort_option))
136
-
137
-                sorting_list.extends(query_result_ordering)
138
-
139
-                pipeline.append({'$match': query_filters})
140
-                if results_field_list is not None:
141
-                    pipeline.append({
142
-                        '$project': SON([{field_name: 1}
143
-                        for field_name in field_list])})
144
-                pipeline.extend(unwinding_list)
145
-                pipeline.append({'$group': grouping_dict})
146
-                pipeline.extend({'$sort': SON(sorting_list)})
147
-                if offset > 0:
148
-                    pipeline.append({'$skip': offset})
149
-                if limit is not None:
150
-                    pipeline.append({'$limit': limit})
151
-
152
-            #results = list()
153
-            for document in cursor:
154
-                results.append(document)
142
+            pipeline = list()
143
+            unwinding_list = list()
144
+            grouping_dict = OrderedDict()
145
+            sorting_list = list()
146
+            for group_param in group:
147
+                field_name = group_param[0]
148
+                field_sort_option = group_param[1]
149
+                sort_option = MONGODB_SORT_OPERATORS_MAP[field_sort_option]
150
+                unwinding_list.append({'$unwind': '$%s' % field_name})
151
+                grouping_dict[field_name] = '$%s' % field_name
152
+                sorting_list.append((field_name, sort_option))
153
+
154
+            sorting_list.extends(query_result_ordering)
155
+
156
+            pipeline.append({'$match': query_filters})
157
+            if results_field_list is not None:
158
+                pipeline.append({
159
+                    '$project': SON([{field_name: 1}
160
+                    for field_name in field_list])})
161
+            pipeline.extend(unwinding_list)
162
+            pipeline.append({'$group': grouping_dict})
163
+            pipeline.extend({'$sort': SON(sorting_list)})
164
+            if offset > 0:
165
+                pipeline.append({'$skip': offset})
166
+            if limit is not None:
167
+                pipeline.append({'$limit': limit})
155
 
168
 
169
+        results = list()
170
+        for document in cursor:
171
+            results.append(document)
172
+        
156
         return results
173
         return results
157
 
174
 
158
     ##@brief Deletes records according to given filters
175
     ##@brief Deletes records according to given filters
193
     def insert_multi(self, target, datas_list):
210
     def insert_multi(self, target, datas_list):
194
         res = self.__collection(target).insert_many(datas_list)
211
         res = self.__collection(target).insert_many(datas_list)
195
         return list(res.inserted_ids)
212
         return list(res.inserted_ids)
213
+    
214
+    ##@brief Act on abstract LeObject child
215
+    #
216
+    #This method is designed to be called by insert, select and delete method
217
+    #when they encounter an abtract class
218
+    #@param target LeObject child class
219
+    #@param filters
220
+    #@param relational_filters
221
+    #@param act function : the caller method
222
+    #@param **kwargs other arguments
223
+    #@return sum of results (if it's an array it will result in a concat)
224
+    def __act_on_abstract(self, target, filters, relational_filters, act, **kwargs):
225
+        result = list() if act == self.select else 0
226
+        if not target.is_abstract():
227
+            target_childs = target
228
+        else:
229
+            target_childs = [tc for tc in target.child_classes()
230
+                if not tc.is_abstract()]
231
+        for target_child in target_childs:
232
+            #Add target_child to filter
233
+            new_filters = copy.copy(filters)
234
+            for i in range(len(filters)):
235
+                fname, op, val = filters[i]
236
+                if fname == CLASS_ID_FIELDNAME:
237
+                    logger.warning("Dirty drop of filter : '%s %s %s'" % (
238
+                        fname, op, val))
239
+                    del(new_filters[i])
240
+            new_filters.append(
241
+                (CLASS_ID_FIELDNAME, '=', target_child.__name__))
242
+            result += act(
243
+                target = target_child,
244
+                filters = new_filters,
245
+                relational_filters = relational_filters,
246
+                **kwargs)
247
+        return result
196
 
248
 
197
     ##@brief Connect to database
249
     ##@brief Connect to database
198
     #@not this method avoid opening two times the same connection using
250
     #@not this method avoid opening two times the same connection using
377
             if fieldname not in res:
429
             if fieldname not in res:
378
                 res[fieldname] = dict()
430
                 res[fieldname] = dict()
379
             if op in res[fieldname]:
431
             if op in res[fieldname]:
380
-                logger.warn("Dropping condition : '%s %s %s'" % (
432
+                logger.warning("Dropping condition : '%s %s %s'" % (
381
                     fieldname, op, value))
433
                     fieldname, op, value))
382
             else:
434
             else:
383
                 res[fieldname][op] = value
435
                 res[fieldname][op] = value
399
         mongoval = value
451
         mongoval = value
400
         #Converting lodel2 wildcarded string into a case insensitive
452
         #Converting lodel2 wildcarded string into a case insensitive
401
         #mongodb re
453
         #mongodb re
402
-        if mongop in cls.mon_op_re:
454
+        if mongop in cls.mongo_op_re:
403
             #unescaping \
455
             #unescaping \
404
             mongoval = value.replace('\\\\','\\')
456
             mongoval = value.replace('\\\\','\\')
405
             if not mongoval.startswith('*'):
457
             if not mongoval.startswith('*'):

Loading…
Cancel
Save