3 from pymongo
import MongoClient
, errors
4 from dbbase
import DbException
, DbBase
5 from http
import HTTPStatus
6 from time
import time
, sleep
8 __author__
= "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
10 # TODO consider use this decorator for database access retries
12 # def retry_mongocall(call):
13 # def _retry_mongocall(*args, **kwargs):
17 # return call(*args, **kwargs)
18 # except pymongo.AutoReconnect as e:
20 # raise DbException(str(e))
22 # return _retry_mongocall
25 class DbMongo(DbBase
):
26 conn_initial_timout
= 120
29 def __init__(self
, logger_name
='db'):
30 self
.logger
= logging
.getLogger(logger_name
)
32 def db_connect(self
, config
):
34 if "logger_name" in config
:
35 self
.logger
= logging
.getLogger(config
["logger_name"])
36 self
.client
= MongoClient(config
["host"], config
["port"])
37 self
.db
= self
.client
[config
["name"]]
38 if "loglevel" in config
:
39 self
.logger
.setLevel(getattr(logging
, config
['loglevel']))
40 # get data to try a connection
44 self
.db
.users
.find_one({"username": "admin"})
46 except errors
.ConnectionFailure
as e
:
47 if time() - now
>= self
.conn_initial_timout
:
49 self
.logger
.info("Waiting to database up {}".format(e
))
51 except errors
.PyMongoError
as e
:
52 raise DbException(str(e
))
54 def db_disconnect(self
):
58 def _format_filter(filter):
61 for query_k
, query_v
in filter.items():
62 dot_index
= query_k
.rfind(".")
63 if dot_index
> 1 and query_k
[dot_index
+1:] in ("eq", "ne", "gt", "gte", "lt", "lte", "cont",
65 operator
= "$" + query_k
[dot_index
+1:]
66 if operator
== "$neq":
68 k
= query_k
[:dot_index
]
74 if isinstance(v
, list):
75 if operator
in ("$eq", "$cont"):
78 elif operator
in ("$ne", "$ncont"):
84 if operator
in ("$eq", "$cont"):
85 # v cannot be a comma separated list, because operator would have been changed to $in
87 elif operator
== "$ncount":
88 # v cannot be a comma separated list, because operator would have been changed to $nin
89 db_filter
[k
] = {"$ne": v
}
91 # maybe db_filter[k] exist. e.g. in the query string for values between 5 and 8: "a.gt=5&a.lt=8"
92 if k
not in db_filter
:
94 db_filter
[k
][operator
] = v
97 except Exception as e
:
98 raise DbException("Invalid query string filter at {}:{}. Error: {}".format(query_k
, v
, e
),
99 http_code
=HTTPStatus
.BAD_REQUEST
)
101 def get_list(self
, table
, filter={}):
104 collection
= self
.db
[table
]
105 rows
= collection
.find(self
._format
_filter
(filter))
111 except Exception as e
: # TODO refine
112 raise DbException(str(e
))
114 def get_one(self
, table
, filter={}, fail_on_empty
=True, fail_on_more
=True):
117 filter = self
._format
_filter
(filter)
118 collection
= self
.db
[table
]
119 if not (fail_on_empty
and fail_on_more
):
120 return collection
.find_one(filter)
121 rows
= collection
.find(filter)
122 if rows
.count() == 0:
124 raise DbException("Not found any {} with filter='{}'".format(table
[:-1], filter),
125 HTTPStatus
.NOT_FOUND
)
127 elif rows
.count() > 1:
129 raise DbException("Found more than one {} with filter='{}'".format(table
[:-1], filter),
132 except Exception as e
: # TODO refine
133 raise DbException(str(e
))
135 def del_list(self
, table
, filter={}):
137 collection
= self
.db
[table
]
138 rows
= collection
.delete_many(self
._format
_filter
(filter))
139 return {"deleted": rows
.deleted_count
}
142 except Exception as e
: # TODO refine
143 raise DbException(str(e
))
145 def del_one(self
, table
, filter={}, fail_on_empty
=True):
147 collection
= self
.db
[table
]
148 rows
= collection
.delete_one(self
._format
_filter
(filter))
149 if rows
.deleted_count
== 0:
151 raise DbException("Not found any {} with filter='{}'".format(table
[:-1], filter),
152 HTTPStatus
.NOT_FOUND
)
154 return {"deleted": rows
.deleted_count
}
155 except Exception as e
: # TODO refine
156 raise DbException(str(e
))
158 def create(self
, table
, indata
):
160 collection
= self
.db
[table
]
161 data
= collection
.insert_one(indata
)
162 return data
.inserted_id
163 except Exception as e
: # TODO refine
164 raise DbException(str(e
))
166 def set_one(self
, table
, filter, update_dict
, fail_on_empty
=True):
168 collection
= self
.db
[table
]
169 rows
= collection
.update_one(self
._format
_filter
(filter), {"$set": update_dict
})
170 if rows
.matched_count
== 0:
172 raise DbException("Not found any {} with filter='{}'".format(table
[:-1], filter),
173 HTTPStatus
.NOT_FOUND
)
175 return {"modified": rows
.modified_count
}
176 except Exception as e
: # TODO refine
177 raise DbException(str(e
))
179 def replace(self
, table
, id, indata
, fail_on_empty
=True):
181 _filter
= {"_id": id}
182 collection
= self
.db
[table
]
183 rows
= collection
.replace_one(_filter
, indata
)
184 if rows
.matched_count
== 0:
186 raise DbException("Not found any {} with filter='{}'".format(table
[:-1], _filter
),
187 HTTPStatus
.NOT_FOUND
)
189 return {"replaced": rows
.modified_count
}
190 except Exception as e
: # TODO refine
191 raise DbException(str(e
))