|
@@ -2,12 +2,15 @@
|
2
|
2
|
|
3
|
3
|
import re
|
4
|
4
|
import warnings
|
|
5
|
+import copy
|
|
6
|
+import operator
|
5
|
7
|
from bson.son import SON
|
6
|
8
|
from collections import OrderedDict
|
7
|
9
|
import pymongo
|
8
|
10
|
from pymongo.errors import BulkWriteError
|
9
|
11
|
|
10
|
12
|
from lodel import logger
|
|
13
|
+from lodel.leapi.leobject import CLASS_ID_FIELDNAME
|
11
|
14
|
|
12
|
15
|
from . import utils
|
13
|
16
|
from .utils import object_collection_name,\
|
|
@@ -75,7 +78,8 @@ class MongoDbDatasource(object):
|
75
|
78
|
target = emcomp.uid_source()
|
76
|
79
|
tuid = target._uid[0] # Multiple UID broken here
|
77
|
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
|
83
|
if len(results) == 0:
|
80
|
84
|
return 1
|
81
|
85
|
return results[0][tuid]+1
|
|
@@ -86,73 +90,86 @@ class MongoDbDatasource(object):
|
86
|
90
|
#@param filters list : List of filters
|
87
|
91
|
#@param rel_filters list : List of relational filters
|
88
|
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
|
95
|
#@param limit int : Number of records to be returned
|
91
|
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
|
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
|
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
|
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
|
173
|
return results
|
157
|
174
|
|
158
|
175
|
##@brief Deletes records according to given filters
|
|
@@ -193,6 +210,41 @@ class MongoDbDatasource(object):
|
193
|
210
|
def insert_multi(self, target, datas_list):
|
194
|
211
|
res = self.__collection(target).insert_many(datas_list)
|
195
|
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
|
249
|
##@brief Connect to database
|
198
|
250
|
#@not this method avoid opening two times the same connection using
|
|
@@ -377,7 +429,7 @@ class MongoDbDatasource(object):
|
377
|
429
|
if fieldname not in res:
|
378
|
430
|
res[fieldname] = dict()
|
379
|
431
|
if op in res[fieldname]:
|
380
|
|
- logger.warn("Dropping condition : '%s %s %s'" % (
|
|
432
|
+ logger.warning("Dropping condition : '%s %s %s'" % (
|
381
|
433
|
fieldname, op, value))
|
382
|
434
|
else:
|
383
|
435
|
res[fieldname][op] = value
|
|
@@ -399,7 +451,7 @@ class MongoDbDatasource(object):
|
399
|
451
|
mongoval = value
|
400
|
452
|
#Converting lodel2 wildcarded string into a case insensitive
|
401
|
453
|
#mongodb re
|
402
|
|
- if mongop in cls.mon_op_re:
|
|
454
|
+ if mongop in cls.mongo_op_re:
|
403
|
455
|
#unescaping \
|
404
|
456
|
mongoval = value.replace('\\\\','\\')
|
405
|
457
|
if not mongoval.startswith('*'):
|