Browse Source

Bugfixes in query and datasources.

Yann Weber 8 years ago
parent
commit
0acd3d6612
3 changed files with 74 additions and 59 deletions
  1. 28
    30
      lodel/leapi/query.py
  2. 45
    22
      plugins/mongodb_datasource/datasource.py
  3. 1
    7
      plugins/mongodb_datasource/utils.py

+ 28
- 30
lodel/leapi/query.py View File

@@ -465,10 +465,8 @@ class LeInsertQuery(LeQuery):
465 465
     # @param new_datas : datas to be inserted
466 466
     def _query(self, datas):
467 467
         datas = self._target_class.prepare_datas(datas, True, False)
468
-        nb_inserted = self._rw_datasource.insert(self._target_class,datas)
469
-        if nb_inserted <= 0:
470
-            raise LeApiQueryError("Insertion error")
471
-        return nb_inserted
468
+        id_inserted = self._rw_datasource.insert(self._target_class,datas)
469
+        return id_inserted
472 470
     """
473 471
     ## @brief Implements an insert query operation, with multiple insertions
474 472
     # @param datas : list of **datas to be inserted
@@ -610,15 +608,15 @@ class LeGetQuery(LeFilteredQuery):
610 608
         super().__init__(target_class, query_filters)
611 609
         
612 610
         ##@brief The fields to get
613
-        self.__field_list = None
611
+        self._field_list = None
614 612
         ##@brief An equivalent to the SQL ORDER BY
615
-        self.__order = None
613
+        self._order = None
616 614
         ##@brief An equivalent to the SQL GROUP BY
617
-        self.__group = None
615
+        self._group = None
618 616
         ##@brief An equivalent to the SQL LIMIT x
619
-        self.__limit = None
617
+        self._limit = None
620 618
         ##@brief An equivalent to the SQL LIMIT x, OFFSET
621
-        self.__offset = 0
619
+        self._offset = 0
622 620
         
623 621
         # Checking kwargs and assigning default values if there is some
624 622
         for argname in kwargs:
@@ -633,22 +631,22 @@ class LeGetQuery(LeFilteredQuery):
633 631
 
634 632
         if 'order' in kwargs:
635 633
             #check kwargs['order']
636
-            self.__order = kwargs['order']
634
+            self._order = kwargs['order']
637 635
         if 'group' in kwargs:
638 636
             #check kwargs['group']
639
-            self.__group = kwargs['group']
637
+            self._group = kwargs['group']
640 638
         if 'limit' in kwargs and kwargs['limit'] is not None:
641 639
             try:
642
-                self.__limit = int(kwargs['limit'])
643
-                if self.__limit <= 0:
640
+                self._limit = int(kwargs['limit'])
641
+                if self._limit <= 0:
644 642
                     raise ValueError()
645 643
             except ValueError:
646 644
                 msg = "limit argument expected to be an interger > 0"
647 645
                 raise ValueError(msg)
648 646
         if 'offset' in kwargs:
649 647
             try:
650
-                self.__offset = int(kwargs['offset'])
651
-                if self.__offset < 0:
648
+                self._offset = int(kwargs['offset'])
649
+                if self._offset < 0:
652 650
                     raise ValueError()
653 651
             except ValueError:
654 652
                 msg = "offset argument expected to be an integer >= 0"
@@ -670,7 +668,7 @@ class LeGetQuery(LeFilteredQuery):
670 668
         if len(err_l) > 0:
671 669
             msg = "Error while setting field_list in a get query"
672 670
             raise LeApiQueryErrors(msg = msg, exceptions = err_l)
673
-        self.__field_list = list(set(field_list))
671
+        self._field_list = list(set(field_list))
674 672
     
675 673
     ##@brief Execute the get query
676 674
     def execute(self, datas = None):
@@ -680,25 +678,25 @@ class LeGetQuery(LeFilteredQuery):
680 678
     # @returns a list containing the item(s)
681 679
     def _query(self, datas = None):
682 680
         # select datas corresponding to query_filter
683
-        l_datas=self._ro_datasource.select(  self._target_class,
684
-                                    list(self.field_list),
685
-                                    self.query_filter,
686
-                                    None,
687
-                                    self.__order,
688
-                                    self.__group,
689
-                                    self.__limit,
690
-                                    self.offset,
691
-                                    False)
681
+        l_datas=self._ro_datasource.select( 
682
+            target = self._target_class,
683
+            field_list = list(self._field_list),
684
+            filters = self._query_filter[0],
685
+            rel_filters = self._query_filter[1],
686
+            order = self._order,
687
+            group = self._group,
688
+            limit = self._limit,
689
+            offset = self._offset)
692 690
         return l_datas
693 691
     
694 692
     ##@return a dict with query infos
695 693
     def dump_infos(self):
696 694
         ret = super().dump_infos()
697
-        ret.update( {   'field_list' : self.__field_list,
698
-                        'order' : self.__order,
699
-                        'group' : self.__group,
700
-                        'limit' : self.__limit,
701
-                        'offset': self.__offset,
695
+        ret.update( {   'field_list' : self._field_list,
696
+                        'order' : self._order,
697
+                        'group' : self._group,
698
+                        'limit' : self._limit,
699
+                        'offset': self._offset,
702 700
         })
703 701
         return ret
704 702
 

+ 45
- 22
plugins/mongodb_datasource/datasource.py View File

@@ -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")

+ 1
- 7
plugins/mongodb_datasource/utils.py View File

@@ -81,13 +81,7 @@ def connect(host, port, db_name, username, password):
81 81
 # @param class_object EmClass
82 82
 # @return str
83 83
 def object_collection_name(class_object):
84
-    if not class_object.pure_abstract:
85
-        class_parent = class_object.parents[0].uid
86
-        collection_name = ("%s%s" % (collection_prefix['object'], class_parent)).lower()
87
-    else:
88
-        collection_name = ("%s%s" % (collection_prefix['object'], class_object.name)).lower()
89
-
90
-    return collection_name
84
+    return class_object.__name__
91 85
 
92 86
 
93 87
 ## @brief Determine a collection field name given a lodel2 fieldname

Loading…
Cancel
Save