|
@@ -82,7 +82,7 @@ class MongoDbDatasource(object):
|
82
|
82
|
collection_name = object_collection_name(target)
|
83
|
83
|
collection = self.database[collection_name]
|
84
|
84
|
query_filters = self.__process_filters(
|
85
|
|
- target, filters, relational_filters)
|
|
85
|
+ target, filters, rel_filters)
|
86
|
86
|
query_result_ordering = None
|
87
|
87
|
if order is not None:
|
88
|
88
|
query_result_ordering = parse_query_order(order)
|
|
@@ -155,15 +155,15 @@ class MongoDbDatasource(object):
|
155
|
155
|
# @param new_datas dict : datas to insert
|
156
|
156
|
# @return the inserted uid
|
157
|
157
|
def insert(self, target, new_datas):
|
158
|
|
- res = self.__collection(target).insert_one(new_datas)
|
159
|
|
- return res.inserted_id
|
|
158
|
+ res = self.__collection(target).insert(new_datas)
|
|
159
|
+ return str(res)
|
160
|
160
|
|
161
|
161
|
## @brief Inserts a list of records in a given collection
|
162
|
162
|
# @param target Emclass : class of the objects inserted
|
163
|
163
|
# @param datas_list list : list of dict
|
164
|
164
|
# @return list : list of the inserted records' ids
|
165
|
165
|
def insert_multi(self, target, datas_list):
|
166
|
|
- res = self.__collection.insert_many(datas_list)
|
|
166
|
+ res = self.__collection(target).insert_many(datas_list)
|
167
|
167
|
return list(result.inserted_ids)
|
168
|
168
|
|
169
|
169
|
##@brief Connect to database
|
|
@@ -225,21 +225,20 @@ class MongoDbDatasource(object):
|
225
|
225
|
#@return a list of pymongo filters ( dict {FIELD:{OPERATOR:VALUE}} )
|
226
|
226
|
def __process_filters(self,target, filters, relational_filters):
|
227
|
227
|
# Simple filters lodel2 -> pymongo converting
|
228
|
|
- res = [convert_filter(filt) for filt in filters]
|
229
|
|
- rfilters = self.__prepare_relational_filters(relational_filters)
|
|
228
|
+ res = self.__filters2mongo(filters)
|
|
229
|
+ rfilters = self.__prepare_relational_filters(target, relational_filters)
|
230
|
230
|
#Now that everything is well organized, begin to forge subquerie
|
231
|
231
|
#filters
|
232
|
|
- subq_filters = self.__subqueries_from_relational_filters(
|
233
|
|
- target, rfilters)
|
|
232
|
+ self.__subqueries_from_relational_filters(target, rfilters)
|
234
|
233
|
# Executing subqueries, creating filters from result, and injecting
|
235
|
234
|
# them in original filters of the query
|
236
|
|
- if len(subq_filters) > 0:
|
|
235
|
+ if len(rfilters) > 0:
|
237
|
236
|
logger.debug("Begining subquery execution")
|
238
|
|
- for fname in subq_filters:
|
|
237
|
+ for fname in rfilters:
|
239
|
238
|
if fname not in res:
|
240
|
239
|
res[fname] = dict()
|
241
|
240
|
subq_results = set()
|
242
|
|
- for leobject, sq_filters in subq_filters[fname].items():
|
|
241
|
+ for leobject, sq_filters in rfilters[fname].items():
|
243
|
242
|
uid_fname = mongo_fieldname(leobject._uid)
|
244
|
243
|
log_msg = "Subquery running on collection {coll} with filters \
|
245
|
244
|
'{filters}'"
|
|
@@ -262,7 +261,7 @@ class MongoDbDatasource(object):
|
262
|
261
|
res[fname]['$in'] = list(deduped)
|
263
|
262
|
else:
|
264
|
263
|
res[fname]['$in'] = list(subq_results)
|
265
|
|
- if len(subq_filters) > 0:
|
|
264
|
+ if len(rfilters) > 0:
|
266
|
265
|
logger.debug("End of subquery execution")
|
267
|
266
|
return res
|
268
|
267
|
|
|
@@ -281,14 +280,15 @@ class MongoDbDatasource(object):
|
281
|
280
|
#@param rfilters dict : A struct as returned by
|
282
|
281
|
#MongoDbDatasource.__prepare_relational_filters()
|
283
|
282
|
#@return None, the rfilters argument is modified by reference
|
284
|
|
- def __subqueries_from_relational_filters(self, target, rfilters):
|
|
283
|
+ @classmethod
|
|
284
|
+ def __subqueries_from_relational_filters(cls, target, rfilters):
|
285
|
285
|
for fname in rfilters:
|
286
|
286
|
for leobject in rfilters[fname]:
|
287
|
287
|
for rfield in rfilters[fname][leobject]:
|
288
|
288
|
#This way of doing is not optimized but allows to trigger
|
289
|
289
|
#warnings in some case (2 different values for a same op
|
290
|
290
|
#on a same field on a same collection)
|
291
|
|
- mongofilters = self.__op_value_listconv(
|
|
291
|
+ mongofilters = cls.__op_value_listconv(
|
292
|
292
|
rfilters[fname][leobject][rfield])
|
293
|
293
|
rfilters[fname][leobject][rfield] = mongofilters
|
294
|
294
|
|
|
@@ -307,7 +307,8 @@ class MongoDbDatasource(object):
|
307
|
307
|
#@param target LeObject subclass (no instance) : Target class
|
308
|
308
|
#@param relational_filters : same composition thant filters except that
|
309
|
309
|
#@return a struct as described above
|
310
|
|
- def __prepare_relational_filters(self, target, relational_filters):
|
|
310
|
+ @classmethod
|
|
311
|
+ def __prepare_relational_filters(cls, target, relational_filters):
|
311
|
312
|
# We are going to regroup relationnal filters by reference field
|
312
|
313
|
# then by collection
|
313
|
314
|
rfilters = dict()
|
|
@@ -334,6 +335,26 @@ class MongoDbDatasource(object):
|
334
|
335
|
rfilters[fname][repr_leo][rfield] = list()
|
335
|
336
|
rfilters[fname][repr_leo][rfield].append((op, value))
|
336
|
337
|
return rfilters
|
|
338
|
+
|
|
339
|
+ ##@brief Convert lodel2 filters to pymongo conditions
|
|
340
|
+ #@param filters list : list of lodel filters
|
|
341
|
+ #@return dict representing pymongo conditions
|
|
342
|
+ @classmethod
|
|
343
|
+ def __filters2mongo(cls, filters):
|
|
344
|
+ res = dict()
|
|
345
|
+ for fieldname, op, value in filters:
|
|
346
|
+ oop = op
|
|
347
|
+ ovalue = value
|
|
348
|
+ op, value = cls.__op_value_conv(op, value)
|
|
349
|
+ if fieldname not in res:
|
|
350
|
+ res[fieldname] = dict()
|
|
351
|
+ if op in res[fieldname]:
|
|
352
|
+ logger.warn("Dropping condition : '%s %s %s'" % (
|
|
353
|
+ fieldname, op, value))
|
|
354
|
+ else:
|
|
355
|
+ res[fieldname][op] = value
|
|
356
|
+ return res
|
|
357
|
+
|
337
|
358
|
|
338
|
359
|
##@brief Convert lodel2 operator and value to pymongo struct
|
339
|
360
|
#
|
|
@@ -341,15 +362,16 @@ class MongoDbDatasource(object):
|
341
|
362
|
#@param op str : take value in LeFilteredQuery::_query_operators
|
342
|
363
|
#@param value mixed : the value
|
343
|
364
|
#@return a tuple(mongo_op, mongo_value)
|
344
|
|
- def __op_value_conv(self, op, value):
|
345
|
|
- if op not in self.lodel2mongo_op_map:
|
|
365
|
+ @classmethod
|
|
366
|
+ def __op_value_conv(cls, op, value):
|
|
367
|
+ if op not in cls.lodel2mongo_op_map:
|
346
|
368
|
msg = "Invalid operator '%s' found" % op
|
347
|
369
|
raise MongoDbDataSourceError(msg)
|
348
|
|
- mongop = self.lodel2mongo_op_map[op]
|
|
370
|
+ mongop = cls.lodel2mongo_op_map[op]
|
349
|
371
|
mongoval = value
|
350
|
372
|
#Converting lodel2 wildcarded string into a case insensitive
|
351
|
373
|
#mongodb re
|
352
|
|
- if mongop in self.mon_op_re:
|
|
374
|
+ if mongop in cls.mon_op_re:
|
353
|
375
|
#unescaping \
|
354
|
376
|
mongoval = value.replace('\\\\','\\')
|
355
|
377
|
if not mongoval.startswith('*'):
|
|
@@ -358,16 +380,17 @@ class MongoDbDatasource(object):
|
358
|
380
|
if not (mongoval[-1] == '*' and mongoval[-2] != '\\'):
|
359
|
381
|
mongoval += '$'
|
360
|
382
|
#Replacing every other unescaped wildcard char
|
361
|
|
- mongoval = self.wildcard_re.sub('.*', mongoval)
|
|
383
|
+ mongoval = cls.wildcard_re.sub('.*', mongoval)
|
362
|
384
|
mongoval = {'$regex': mongoval, '$options': 'i'}
|
363
|
385
|
return (op, mongoval)
|
364
|
386
|
|
365
|
387
|
##@brief Convert a list of tuple(OP, VALUE) into a pymongo filter dict
|
366
|
388
|
#@return a dict with mongo op as key and value as value...
|
367
|
|
- def __op_value_listconv(self, op_value_list):
|
|
389
|
+ @classmethod
|
|
390
|
+ def __op_value_listconv(cls, op_value_list):
|
368
|
391
|
result = dict()
|
369
|
392
|
for op, value in op_value_list:
|
370
|
|
- mongop, mongoval = self.__op_value_conv(op, value)
|
|
393
|
+ mongop, mongoval = cls.__op_value_conv(op, value)
|
371
|
394
|
if mongop in result:
|
372
|
395
|
warnings.warn("Duplicated value given for a single \
|
373
|
396
|
field/operator couple in a query. We will keep only the first one")
|