Browse Source

Datasource implementation

- Implementation of the delete, insert and insert_multi methods + first implementation of the select method in the MongoDB datasource
- Added parsing methods in the mongodb/utils module
Roland Haroutiounian 9 years ago
parent
commit
d28828c7c9
2 changed files with 119 additions and 15 deletions
  1. 43
    15
      lodel/datasource/mongodb/datasource.py
  2. 76
    0
      lodel/datasource/mongodb/utils.py

+ 43
- 15
lodel/datasource/mongodb/datasource.py View File

@@ -56,39 +56,67 @@ class MongoDbDataSource(object):
56 56
         return (connection_args['login'], urllib.quote_plus(connection_args['password']), connection_args['host'],
57 57
                 connection_args['port'], connection_args['dbname'])
58 58
 
59
+
60
+    ## @brief returns a selection of documents from the datasource
61
+    # @param target_cls Emclass
62
+    # @param field_list list
59 63
     def select(self, target_cls, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0,
60 64
                instanciate=True):
61
-        pass
62
-
65
+        collection_name = utils.object_collection_name(target_cls.__class__)
66
+        collection = self.database[collection_name]
67
+        query_filters = utils.parse_query_filters(filters)
68
+        query_result_ordering = utils.parse_query_order(order) if order is not None else None
69
+        results_field_list = None if len(field_list) == 0 else field_list  # TODO On peut peut-être utiliser None dans les arguments au lieu d'une liste vide
70
+        limit = limit if limit is not None else 0
71
+        cursor = collection.find(
72
+            filter=query_filters,
73
+            projection=results_field_list,
74
+            skip=offset,
75
+            limit=limit,
76
+            sort=query_result_ordering
77
+        )
78
+        results = list()
79
+        for document in cursor:
80
+            results.append(document)
81
+
82
+        return results
83
+
84
+    ## @brief Deletes one record defined by its uid
85
+    # @param target_cls Emclass : class of the record to delete
86
+    # @param uid dict|list : a dictionary of fields and values composing the unique identifier of the record or a list of several dictionaries
87
+    # @return int : number of deleted records
88
+    # @TODO check the content of the result.raw_result property depending on the informations to return
89
+    # @TODO Implement the error management
63 90
     def delete(self, target_cls, uid):
64
-        pass
91
+        if isinstance(uid, dict):
92
+            uid = [uid]
93
+        collection_name = utils.object_collection_name(target_cls.__class__)
94
+        collection = self.database[collection_name]
95
+        result = collection.delete_many(uid)
96
+        return result.deleted_count
65 97
 
66 98
     def update(self, target_cls, uid, **datas):
99
+
67 100
         pass
68 101
 
69 102
     ## @brief Inserts a record in a given collection
70 103
     # @param target_cls Emclass : class of the object to insert
71 104
     # @param datas dict : datas to insert
72
-    # @return ObjectId : the uid of the inserted record
105
+    # @return bool
106
+    # @TODO Implement the error management
73 107
     def insert(self, target_cls, **datas):
74 108
         collection_name = utils.object_collection_name(target_cls.__class__)
75 109
         collection = self.database[collection_name]
76 110
         result = collection.insert_one(datas)
77
-        return result.inserted_id
111
+        return len(result.inserted_id)
78 112
 
79 113
     ## @brief Inserts a list of records in a given collection
80 114
     # @param target_cls Emclass : class of the objects inserted
81 115
     # @param datas_list
82
-    # @return int : Number of inserted records in the collection
116
+    # @return list : list of the inserted records' ids
117
+    # @TODO Implement the error management
83 118
     def insert_multi(self, target_cls, datas_list):
84 119
         collection_name = utils.object_collection_name(target_cls.__class__)
85 120
         collection = self.database[collection_name]
86
-        bulk = collection.initialize_ordered_bulk_op()
87
-        for datas_list_item in datas_list:
88
-            bulk.insert(datas_list_item)
89
-        try:
90
-            result = bulk.execute()
91
-        except BulkWriteError as bwe:
92
-            pass  # TODO add the behavior in case of an exception => bwe.details['writeErrors'], is a list of error info dicts
93
-
94
-        return result['nInserted']
121
+        result = collection.insert_many(datas_list)
122
+        return len(result.inserted_ids)

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

@@ -1,13 +1,89 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3
+import pymongo
4
+
3 5
 collection_prefix = {
4 6
     'relation': 'rel_',
5 7
     'collection': 'class_'
6 8
 }
7 9
 
10
+# TODO Ajouter ici les conversions vers les opérateurs MongoDB correspondants
11
+LODEL_OPERATORS_MAP = {
12
+    '=': {'name': '$eq', 'value_type': None},
13
+    '<=': {'name': '$lte', 'value_type': None},
14
+    '>=': {'name': '$gte', 'value_type': None},
15
+    '!=': {'name': '$ne', 'value_type': None},
16
+    '<': {'name': '$lt', 'value_type': None},
17
+    '>': {'name': '$gt', 'value_type': None},
18
+    ' in ': {'name': '$in', 'value_type': list},
19
+    ' not in ': {'name': '$nin', 'value_type': list},
20
+    ' like ': {'name': '$eq', 'value_type': str},
21
+    ' not like ': {'name': '', 'value_type': str},  # TODO Add the operator
22
+    'OR': {'name': '$or', 'value_type': list},
23
+    'AND': {'name': '$and', 'value_type': list}
24
+}
25
+
26
+LODEL_SORT_OPERATORS_MAP = {
27
+    'ASC': pymongo.ASCENDING,
28
+    'DESC': pymongo.DESCENDING
29
+}
30
+
8 31
 
9 32
 ## @brief Returns a collection name given a Emclass name
10 33
 # @param class_name str : The class name
11 34
 # @return str
12 35
 def object_collection_name(class_name):
13 36
     return ("%s%s" % (collection_prefix['object'], class_name)).lower()
37
+
38
+
39
+## @brief converts the query filters into MongoDB filters
40
+# @param query_filters list
41
+# @return dict
42
+# @todo refactor this function by adding a return_type argument (default= dict) which can be a dict or a list, then delete the convert_filter_list function
43
+def parse_query_filters(query_filters):
44
+    filters_dict = dict()
45
+    for query_filter in query_filters:
46
+        if isinstance(query_filter, tuple):
47
+            filters_dict.update(convert_filter(query_filter))
48
+        elif isinstance(query_filter, dict):
49
+            query_item = list(query_filter.items())[0]
50
+            key = LODEL_OPERATORS_MAP[query_item[0]]
51
+            filters_dict.update({key: convert_filter_list(query_item[1])})
52
+        else:
53
+            # TODO Add an exception management here in case the filter is neither a tuple nor a dict
54
+            pass
55
+    return filters_dict
56
+
57
+
58
+## @brief converts a query filters list into MongoDB filters list
59
+#   It is used mainly in case of an "AND" or an "OR"
60
+# @param filters_list list
61
+# @return list
62
+def convert_filter_list(filters_list):
63
+    converted_filters_list = list()
64
+    for filter_list_item in filters_list:
65
+        if isinstance(filter_list_item, tuple):
66
+            converted_filters_list.append(convert_filter(filter_list_item))
67
+        elif isinstance(filter_list_item, dict):
68
+            query_item = list(filter_list_item.items())[0]
69
+            key = LODEL_OPERATORS_MAP[query_item[0]]['name']
70
+            converted_filters_list.append({key: convert_filter_list(query_item[1])})
71
+    return converted_filters_list
72
+
73
+## @brief converts a Lodel query filter into a MongoDB filter
74
+# @param filter tuple : (FIELD, OPERATOR, VALUE) representing the query filter to convert
75
+# @return dict : {KEY: {OPERATOR:VALUE}}
76
+# @todo Add an error management for the operator mismatch
77
+# @todo Add the checks for the type of values authorized in certain mongodb operators, such "$in" for example which takes a list
78
+def convert_filter(filter):
79
+    key, operator, value = filter
80
+    converted_operator = LODEL_OPERATORS_MAP[operator]['name']
81
+    converted_filter = {key: {converted_operator: value}}
82
+    return converted_filter
83
+
84
+## @brief Returns a list of sorting options
85
+# @param query_filters_order list
86
+# @return list
87
+def parse_query_order(query_filters_order):
88
+    ordering = list()
89
+    return ordering

Loading…
Cancel
Save