import logging
from pymongo import MongoClient, errors
-from dbbase import DbException, DbBase
+from osm_common.dbbase import DbException, DbBase
from http import HTTPStatus
from time import time, sleep
+from copy import deepcopy
__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
# return _retry_mongocall
+def deep_update(to_update, update_with):
+ """
+ Update 'to_update' dict with the content 'update_with' dict recursively
+ :param to_update: must be a dictionary to be modified
+ :param update_with: must be a dictionary. It is not changed
+ :return: to_update
+ """
+ for key in update_with:
+ if key in to_update:
+ if isinstance(to_update[key], dict) and isinstance(update_with[key], dict):
+ deep_update(to_update[key], update_with[key])
+ continue
+ to_update[key] = deepcopy(update_with[key])
+ return to_update
+
+
class DbMongo(DbBase):
conn_initial_timout = 120
conn_timout = 10
pass # TODO
@staticmethod
- def _format_filter(filter):
+ def _format_filter(q_filter):
+ """
+ Translate query string filter into mongo database filter
+ :param q_filter: Query string content. Follows SOL005 section 4.3.2 guidelines, with the follow extensions and
+ differences: It accpept ".nq" (not equal) in addition to ".neq".
+ For arrays if get an item with exact matching. For partial matching, use the special work ".ANYINDEX.".
+ :return: database mongo filter
+ """
try:
db_filter = {}
- for query_k, query_v in filter.items():
+ for query_k, query_v in q_filter.items():
dot_index = query_k.rfind(".")
if dot_index > 1 and query_k[dot_index+1:] in ("eq", "ne", "gt", "gte", "lt", "lte", "cont",
"ncont", "neq"):
- operator = "$" + query_k[dot_index+1:]
+ operator = "$" + query_k[dot_index + 1:]
if operator == "$neq":
operator = "$ne"
k = query_k[:dot_index]
if operator in ("$eq", "$cont"):
# v cannot be a comma separated list, because operator would have been changed to $in
- db_filter[k] = v
+ db_v = v
elif operator == "$ncount":
# v cannot be a comma separated list, because operator would have been changed to $nin
- db_filter[k] = {"$ne": v}
+ db_v = {"$ne": v}
else:
- # maybe db_filter[k] exist. e.g. in the query string for values between 5 and 8: "a.gt=5&a.lt=8"
- if k not in db_filter:
- db_filter[k] = {}
- db_filter[k][operator] = v
+ db_v = {operator: v}
+
+ # process the ANYINDEX word at k
+ kleft, _, kright = k.rpartition(".ANYINDEX.")
+ while kleft:
+ k = kleft
+ db_v = {"$elemMatch": {kright: db_v}}
+ kleft, _, kright = k.rpartition(".ANYINDEX.")
+
+ # insert in db_filter
+ # maybe db_filter[k] exist. e.g. in the query string for values between 5 and 8: "a.gt=5&a.lt=8"
+ deep_update(db_filter, {k: db_v})
return db_filter
except Exception as e:
def get_list(self, table, filter={}):
try:
- l = []
+ result = []
collection = self.db[table]
- rows = collection.find(self._format_filter(filter))
+ db_filter = self._format_filter(filter)
+ rows = collection.find(db_filter)
for row in rows:
- l.append(row)
- return l
+ result.append(row)
+ return result
except DbException:
raise
except Exception as e: # TODO refine
try:
collection = self.db[table]
rows = collection.update_one(self._format_filter(filter), {"$set": update_dict})
- if rows.updated_count == 0:
+ if rows.matched_count == 0:
if fail_on_empty:
raise DbException("Not found any {} with filter='{}'".format(table[:-1], filter),
HTTPStatus.NOT_FOUND)
return None
- return {"deleted": rows.deleted_count}
+ return {"modified": rows.modified_count}
except Exception as e: # TODO refine
raise DbException(str(e))
raise DbException("Not found any {} with filter='{}'".format(table[:-1], _filter),
HTTPStatus.NOT_FOUND)
return None
- return {"replace": rows.modified_count}
+ return {"replaced": rows.modified_count}
except Exception as e: # TODO refine
raise DbException(str(e))