MindsDB 25.8.2.0__py3-none-any.whl → 25.9.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of MindsDB might be problematic. Click here for more details.

Files changed (101) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +5 -45
  3. mindsdb/api/a2a/__init__.py +52 -0
  4. mindsdb/api/a2a/agent.py +17 -28
  5. mindsdb/api/a2a/common/server/server.py +17 -36
  6. mindsdb/api/a2a/common/server/task_manager.py +14 -28
  7. mindsdb/api/a2a/common/types.py +3 -4
  8. mindsdb/api/a2a/task_manager.py +43 -55
  9. mindsdb/api/a2a/utils.py +63 -0
  10. mindsdb/api/common/middleware.py +106 -0
  11. mindsdb/api/http/initialize.py +13 -15
  12. mindsdb/api/http/namespaces/agents.py +6 -7
  13. mindsdb/api/http/namespaces/auth.py +6 -14
  14. mindsdb/api/http/namespaces/config.py +0 -2
  15. mindsdb/api/http/namespaces/default.py +74 -106
  16. mindsdb/api/http/start.py +25 -44
  17. mindsdb/api/litellm/start.py +11 -10
  18. mindsdb/api/mcp/__init__.py +165 -0
  19. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +33 -64
  20. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +86 -85
  21. mindsdb/integrations/handlers/crate_handler/crate_handler.py +3 -7
  22. mindsdb/integrations/handlers/derby_handler/derby_handler.py +32 -34
  23. mindsdb/integrations/handlers/documentdb_handler/requirements.txt +1 -0
  24. mindsdb/integrations/handlers/dummy_data_handler/dummy_data_handler.py +12 -13
  25. mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +45 -44
  26. mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +101 -95
  27. mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +129 -129
  28. mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +59 -43
  29. mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +38 -39
  30. mindsdb/integrations/handlers/informix_handler/informix_handler.py +5 -18
  31. mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +22 -28
  32. mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +3 -7
  33. mindsdb/integrations/handlers/mongodb_handler/mongodb_handler.py +53 -67
  34. mindsdb/integrations/handlers/mongodb_handler/requirements.txt +1 -0
  35. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_ast.py +43 -68
  36. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_parser.py +17 -25
  37. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_query.py +10 -16
  38. mindsdb/integrations/handlers/mongodb_handler/utils/mongodb_render.py +43 -69
  39. mindsdb/integrations/libs/base.py +1 -1
  40. mindsdb/interfaces/agents/constants.py +17 -2
  41. mindsdb/interfaces/agents/langchain_agent.py +83 -18
  42. mindsdb/interfaces/knowledge_base/controller.py +3 -1
  43. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +7 -1
  44. mindsdb/interfaces/skills/skill_tool.py +7 -1
  45. mindsdb/interfaces/skills/sql_agent.py +6 -2
  46. mindsdb/utilities/config.py +3 -155
  47. mindsdb/utilities/fs.py +10 -4
  48. mindsdb/utilities/log.py +0 -25
  49. mindsdb/utilities/starters.py +0 -39
  50. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/METADATA +265 -263
  51. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/RECORD +54 -98
  52. mindsdb/api/a2a/__main__.py +0 -144
  53. mindsdb/api/a2a/run_a2a.py +0 -86
  54. mindsdb/api/common/check_auth.py +0 -42
  55. mindsdb/api/http/gunicorn_wrapper.py +0 -17
  56. mindsdb/api/mcp/start.py +0 -205
  57. mindsdb/api/mongo/__init__.py +0 -0
  58. mindsdb/api/mongo/classes/__init__.py +0 -5
  59. mindsdb/api/mongo/classes/query_sql.py +0 -19
  60. mindsdb/api/mongo/classes/responder.py +0 -45
  61. mindsdb/api/mongo/classes/responder_collection.py +0 -34
  62. mindsdb/api/mongo/classes/scram.py +0 -86
  63. mindsdb/api/mongo/classes/session.py +0 -23
  64. mindsdb/api/mongo/functions/__init__.py +0 -19
  65. mindsdb/api/mongo/responders/__init__.py +0 -73
  66. mindsdb/api/mongo/responders/add_shard.py +0 -13
  67. mindsdb/api/mongo/responders/aggregate.py +0 -90
  68. mindsdb/api/mongo/responders/buildinfo.py +0 -17
  69. mindsdb/api/mongo/responders/coll_stats.py +0 -63
  70. mindsdb/api/mongo/responders/company_id.py +0 -25
  71. mindsdb/api/mongo/responders/connection_status.py +0 -22
  72. mindsdb/api/mongo/responders/count.py +0 -21
  73. mindsdb/api/mongo/responders/db_stats.py +0 -32
  74. mindsdb/api/mongo/responders/delete.py +0 -105
  75. mindsdb/api/mongo/responders/describe.py +0 -23
  76. mindsdb/api/mongo/responders/end_sessions.py +0 -13
  77. mindsdb/api/mongo/responders/find.py +0 -175
  78. mindsdb/api/mongo/responders/get_cmd_line_opts.py +0 -18
  79. mindsdb/api/mongo/responders/get_free_monitoring_status.py +0 -14
  80. mindsdb/api/mongo/responders/get_parameter.py +0 -23
  81. mindsdb/api/mongo/responders/getlog.py +0 -14
  82. mindsdb/api/mongo/responders/host_info.py +0 -28
  83. mindsdb/api/mongo/responders/insert.py +0 -270
  84. mindsdb/api/mongo/responders/is_master.py +0 -20
  85. mindsdb/api/mongo/responders/is_master_lower.py +0 -13
  86. mindsdb/api/mongo/responders/list_collections.py +0 -55
  87. mindsdb/api/mongo/responders/list_databases.py +0 -37
  88. mindsdb/api/mongo/responders/list_indexes.py +0 -22
  89. mindsdb/api/mongo/responders/ping.py +0 -13
  90. mindsdb/api/mongo/responders/recv_chunk_start.py +0 -13
  91. mindsdb/api/mongo/responders/replsetgetstatus.py +0 -13
  92. mindsdb/api/mongo/responders/sasl_continue.py +0 -34
  93. mindsdb/api/mongo/responders/sasl_start.py +0 -33
  94. mindsdb/api/mongo/responders/update_range_deletions.py +0 -12
  95. mindsdb/api/mongo/responders/whatsmyuri.py +0 -18
  96. mindsdb/api/mongo/server.py +0 -388
  97. mindsdb/api/mongo/start.py +0 -15
  98. mindsdb/api/mongo/utilities/__init__.py +0 -0
  99. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/WHEEL +0 -0
  100. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/licenses/LICENSE +0 -0
  101. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/top_level.txt +0 -0
@@ -6,9 +6,8 @@ from mindsdb_sql_parser.ast import OrderBy, Identifier, Star, Select, Constant,
6
6
 
7
7
 
8
8
  class MongoToAst:
9
-
10
9
  """
11
- Converts query mongo to AST format
10
+ Converts query mongo to AST format
12
11
  """
13
12
 
14
13
  def from_mongoqeury(self, query):
@@ -19,24 +18,22 @@ class MongoToAst:
19
18
  filter, projection = None, None
20
19
  sort, limit, skip = None, None, None
21
20
  for step in query.pipeline:
22
- if step['method'] == 'find':
23
- filter = step['args'][0]
21
+ if step["method"] == "find":
22
+ filter = step["args"][0]
24
23
  if len(step) > 1:
25
- projection = step['args'][1]
26
- elif step['method'] == 'sort':
27
- sort = step['args'][0]
28
- elif step['method'] == 'limit':
29
- limit = step['args'][0]
30
- elif step['method'] == 'skip':
31
- skip = step['args'][0]
32
-
33
- return self.find(collection, filter=filter,
34
- sort=sort, projection=projection,
35
- limit=limit, skip=skip)
36
-
37
- def find(self, collection: t.Union[list, str],
38
- filter=None, sort=None, projection=None,
39
- limit=None, skip=None, **kwargs):
24
+ projection = step["args"][1]
25
+ elif step["method"] == "sort":
26
+ sort = step["args"][0]
27
+ elif step["method"] == "limit":
28
+ limit = step["args"][0]
29
+ elif step["method"] == "skip":
30
+ skip = step["args"][0]
31
+
32
+ return self.find(collection, filter=filter, sort=sort, projection=projection, limit=limit, skip=skip)
33
+
34
+ def find(
35
+ self, collection: t.Union[list, str], filter=None, sort=None, projection=None, limit=None, skip=None, **kwargs
36
+ ):
40
37
  # https://www.mongodb.com/docs/v4.2/reference/method/db.collection.find/
41
38
 
42
39
  order_by = None
@@ -44,12 +41,7 @@ class MongoToAst:
44
41
  # sort is dict
45
42
  order_by = []
46
43
  for col, direction in sort.items():
47
- order_by.append(
48
- OrderBy(
49
- field=Identifier(parts=[col]),
50
- direction='DESC' if direction == -1 else 'ASC'
51
- )
52
- )
44
+ order_by.append(OrderBy(field=Identifier(parts=[col]), direction="DESC" if direction == -1 else "ASC"))
53
45
 
54
46
  if projection is not None:
55
47
  targets = []
@@ -59,9 +51,7 @@ class MongoToAst:
59
51
  alias = Identifier(parts=[alias])
60
52
  else:
61
53
  alias = None
62
- targets.append(
63
- Identifier(path_str=col, alias=alias)
64
- )
54
+ targets.append(Identifier(path_str=col, alias=alias))
65
55
  else:
66
56
  targets = [Star()]
67
57
 
@@ -92,13 +82,13 @@ class MongoToAst:
92
82
 
93
83
  def convert_filter(self, filter):
94
84
  cond_ops = {
95
- '$and': 'and',
96
- '$or': 'or',
85
+ "$and": "and",
86
+ "$or": "or",
97
87
  }
98
88
 
99
89
  ast_filter = None
100
90
  for k, v in filter.items():
101
- if k in ('$or', '$and'):
91
+ if k in ("$or", "$and"):
102
92
  # suppose it is one key in dict
103
93
 
104
94
  op = cond_ops[k]
@@ -116,7 +106,7 @@ class MongoToAst:
116
106
  arg1 = BinaryOperation(op=op, args=[arg1, node])
117
107
 
118
108
  return arg1
119
- if k in ('$where', '$expr'):
109
+ if k in ("$where", "$expr"):
120
110
  # try to parse simple expression like 'this.saledate > this.latest'
121
111
  return MongoWhereParser(v).to_ast()
122
112
 
@@ -129,25 +119,12 @@ class MongoToAst:
129
119
  if ast_filter is None:
130
120
  ast_filter = ast_com
131
121
  else:
132
- ast_filter = BinaryOperation(op='and', args=[
133
- ast_filter,
134
- ast_com
135
- ])
122
+ ast_filter = BinaryOperation(op="and", args=[ast_filter, ast_com])
136
123
  return ast_filter
137
124
 
138
125
  def handle_filter(self, value):
139
- ops = {
140
- '$ge': '>=',
141
- '$gt': '>',
142
- '$lt': '<',
143
- '$le': '<=',
144
- '$ne': '!=',
145
- '$eq': '='
146
- }
147
- in_ops = {
148
- '$in': 'in',
149
- '$nin': 'not in'
150
- }
126
+ ops = {"$ge": ">=", "$gt": ">", "$lt": "<", "$le": "<=", "$ne": "!=", "$eq": "="}
127
+ in_ops = {"$in": "in", "$nin": "not in"}
151
128
 
152
129
  if isinstance(value, dict):
153
130
  key, value = list(value.items())[0]
@@ -158,18 +135,18 @@ class MongoToAst:
158
135
  if key in in_ops:
159
136
  op = in_ops[key]
160
137
  if not isinstance(value, list):
161
- raise NotImplementedError(f'Unknown type {key}, {value}')
138
+ raise NotImplementedError(f"Unknown type {key}, {value}")
162
139
  value = Tuple(value)
163
140
 
164
141
  return op, value
165
142
 
166
- raise NotImplementedError(f'Unknown type {key}')
143
+ raise NotImplementedError(f"Unknown type {key}")
167
144
 
168
145
  elif isinstance(value, list):
169
- raise NotImplementedError(f'Unknown filter {value}')
146
+ raise NotImplementedError(f"Unknown filter {value}")
170
147
  else:
171
148
  # is simple type
172
- op = '='
149
+ op = "="
173
150
  value = value
174
151
  return op, value
175
152
 
@@ -181,13 +158,12 @@ class MongoWhereParser:
181
158
  def to_ast(self):
182
159
  # parse as python string
183
160
  # replace '=' with '=='
184
- query = re.sub(r'([^=><])=([^=])', r'\1==\2', self.query)
161
+ query = re.sub(r"([^=><])=([^=])", r"\1==\2", self.query)
185
162
 
186
- tree = py_ast.parse(query, mode='eval')
163
+ tree = py_ast.parse(query, mode="eval")
187
164
  return self.process(tree.body)
188
165
 
189
166
  def process(self, node):
190
-
191
167
  if isinstance(node, py_ast.BoolOp):
192
168
  # is AND or OR
193
169
  op = node.op.__class__.__name__
@@ -202,7 +178,7 @@ class MongoWhereParser:
202
178
  if isinstance(node, py_ast.Compare):
203
179
  # it is
204
180
  if len(node.ops) != 1:
205
- raise NotImplementedError(f'Multiple ops {node.ops}')
181
+ raise NotImplementedError(f"Multiple ops {node.ops}")
206
182
  op = self.compare_op(node.ops[0])
207
183
  arg1 = self.process(node.left)
208
184
  arg2 = self.process(node.comparators[0])
@@ -210,7 +186,7 @@ class MongoWhereParser:
210
186
 
211
187
  if isinstance(node, py_ast.Name):
212
188
  # is special operator: latest, ...
213
- if node.id == 'latest':
189
+ if node.id == "latest":
214
190
  return Latest()
215
191
 
216
192
  if isinstance(node, py_ast.Constant):
@@ -228,28 +204,27 @@ class MongoWhereParser:
228
204
 
229
205
  if isinstance(node, py_ast.Attribute):
230
206
  # is 'this.field' - is attribute
231
- if node.value.id != 'this':
232
- raise NotImplementedError(f'Unknown variable {node.value.id}')
207
+ if node.value.id != "this":
208
+ raise NotImplementedError(f"Unknown variable {node.value.id}")
233
209
  return Identifier(parts=[node.attr])
234
210
 
235
- raise NotImplementedError(f'Unknown node {node}')
211
+ raise NotImplementedError(f"Unknown node {node}")
236
212
 
237
213
  def compare_op(self, op):
238
-
239
214
  opname = op.__class__.__name__
240
215
 
241
216
  # TODO: in, not
242
217
 
243
218
  ops = {
244
- 'Eq': '=',
245
- 'NotEq': '!=',
246
- 'Gt': '>',
247
- 'Lt': '<',
248
- 'GtE': '>=',
249
- 'LtE': '<=',
219
+ "Eq": "=",
220
+ "NotEq": "!=",
221
+ "Gt": ">",
222
+ "Lt": "<",
223
+ "GtE": ">=",
224
+ "LtE": "<=",
250
225
  }
251
226
  if opname not in ops:
252
- raise NotImplementedError(f'Unknown $where op: {opname}')
227
+ raise NotImplementedError(f"Unknown $where op: {opname}")
253
228
  return ops[opname]
254
229
 
255
230
  @staticmethod
@@ -1,4 +1,3 @@
1
-
2
1
  import ast as py_ast
3
2
 
4
3
  import dateutil.parser
@@ -8,30 +7,27 @@ from .mongodb_query import MongoQuery
8
7
 
9
8
 
10
9
  class MongodbParser:
11
- '''
12
- Converts string into MongoQuery
13
- '''
10
+ """
11
+ Converts string into MongoQuery
12
+ """
14
13
 
15
14
  def from_string(self, call_str):
16
- tree = py_ast.parse(call_str.strip(), mode='eval')
15
+ tree = py_ast.parse(call_str.strip(), mode="eval")
17
16
  calls = self.process(tree.body)
18
17
  # first call contents collection
19
- method1 = calls[0]['method']
18
+ method1 = calls[0]["method"]
20
19
  if len(method1) < 2:
21
- raise IndexError('Collection not found')
20
+ raise IndexError("Collection not found")
22
21
  collection = method1[-2]
23
22
 
24
23
  mquery = MongoQuery(collection)
25
24
 
26
25
  # keep only last name
27
- calls[0]['method'] = [method1[-1]]
26
+ calls[0]["method"] = [method1[-1]]
28
27
 
29
28
  # convert method names: get first item of list
30
29
  for c in calls:
31
- mquery.add_step({
32
- 'method': c['method'][0],
33
- 'args': c['args']
34
- })
30
+ mquery.add_step({"method": c["method"][0], "args": c["args"]})
35
31
 
36
32
  return mquery
37
33
 
@@ -49,20 +45,17 @@ class MongodbParser:
49
45
  func = node.func.id
50
46
 
51
47
  # special functions:
52
- if func == 'ISODate':
48
+ if func == "ISODate":
53
49
  return dateutil.parser.isoparse(args[0])
54
- if func == 'ObjectId':
50
+ if func == "ObjectId":
55
51
  return ObjectId(args[0])
56
52
  elif isinstance(node.func, py_ast.Attribute):
57
53
  # it can be an attribute or pipeline
58
54
  previous_call, func = self.process_func_name(node.func)
59
55
  else:
60
- raise NotImplementedError(f'Unknown function type: {node.func}')
56
+ raise NotImplementedError(f"Unknown function type: {node.func}")
61
57
 
62
- call = [{
63
- 'method': func,
64
- 'args': args
65
- }]
58
+ call = [{"method": func, "args": args}]
66
59
  if previous_call is not None:
67
60
  call = previous_call + call
68
61
 
@@ -75,7 +68,6 @@ class MongodbParser:
75
68
  return elements
76
69
 
77
70
  if isinstance(node, py_ast.Dict):
78
-
79
71
  keys = []
80
72
  for node2 in node.keys:
81
73
  if isinstance(node2, py_ast.Constant):
@@ -85,7 +77,7 @@ class MongodbParser:
85
77
  elif isinstance(node2, py_ast.Name):
86
78
  value = node2.id
87
79
  else:
88
- raise NotImplementedError(f'Unknown dict key {node2}')
80
+ raise NotImplementedError(f"Unknown dict key {node2}")
89
81
 
90
82
  keys.append(value)
91
83
 
@@ -98,11 +90,11 @@ class MongodbParser:
98
90
  if isinstance(node, py_ast.Name):
99
91
  # special attributes
100
92
  name = node.id
101
- if name == 'true':
93
+ if name == "true":
102
94
  return True
103
- elif name == 'false':
95
+ elif name == "false":
104
96
  return False
105
- elif name == 'null':
97
+ elif name == "null":
106
98
  return None
107
99
 
108
100
  if isinstance(node, py_ast.Constant):
@@ -122,7 +114,7 @@ class MongodbParser:
122
114
  value = self.process(node.operand)
123
115
  return -value
124
116
 
125
- raise NotImplementedError(f'Unknown node {node}')
117
+ raise NotImplementedError(f"Unknown node {node}")
126
118
 
127
119
  def process_func_name(self, node):
128
120
  previous_call = None
@@ -4,17 +4,15 @@ from bson import ObjectId
4
4
 
5
5
 
6
6
  class MongoJSONEncoder(json.JSONEncoder):
7
-
8
7
  def default(self, obj):
9
8
  if isinstance(obj, dt.datetime):
10
- return f'ISODate({obj.isoformat()})'
9
+ return f"ISODate({obj.isoformat()})"
11
10
  if isinstance(obj, ObjectId):
12
- return f'ObjectId({str(obj)})'
11
+ return f"ObjectId({str(obj)})"
13
12
  return super(MongoJSONEncoder, self).default(obj)
14
13
 
15
14
 
16
15
  class MongoQuery:
17
-
18
16
  def __init__(self, collection, pipline=None):
19
17
  self.collection = collection
20
18
  self.pipeline = []
@@ -30,10 +28,8 @@ class MongoQuery:
30
28
  # 'args': [{c: 3}]
31
29
  # }
32
30
 
33
- if 'method' not in step \
34
- or 'args' not in step \
35
- or not isinstance(step['args'], list):
36
- raise AttributeError(f'Wrong step {step}')
31
+ if "method" not in step or "args" not in step or not isinstance(step["args"], list):
32
+ raise AttributeError(f"Wrong step {step}")
37
33
 
38
34
  self.pipeline.append(step)
39
35
 
@@ -43,10 +39,8 @@ class MongoQuery:
43
39
  def __getattr__(self, item):
44
40
  # return callback to save step of pipeline
45
41
  def fnc(*args):
46
- self.pipeline.append({
47
- 'method': item,
48
- 'args': args
49
- })
42
+ self.pipeline.append({"method": item, "args": args})
43
+
50
44
  return fnc
51
45
 
52
46
  def __str__(self):
@@ -72,13 +66,13 @@ class MongoQuery:
72
66
  "db_test.fish.find({a:1}, {b:2}).sort({c:3})"
73
67
  """
74
68
 
75
- call_str = f'db.{self.collection}'
69
+ call_str = f"db.{self.collection}"
76
70
  for step in self.pipeline:
77
71
  args_str = []
78
- for arg in step['args']:
72
+ for arg in step["args"]:
79
73
  args_str.append(MongoJSONEncoder().encode(arg))
80
- call_str += f'.{step["method"]}({",".join(args_str)})'
74
+ call_str += f".{step['method']}({','.join(args_str)})"
81
75
  return call_str
82
76
 
83
77
  def __repr__(self):
84
- return f'MongoQuery({self.collection}, {str(self.pipeline)})'
78
+ return f"MongoQuery({self.collection}, {str(self.pipeline)})"
@@ -5,7 +5,7 @@ from bson.objectid import ObjectId
5
5
  from mindsdb_sql_parser.ast import Select, Update, Identifier, Star, Constant, Tuple, BinaryOperation, Latest, TypeCast
6
6
  from mindsdb_sql_parser.ast.base import ASTNode
7
7
 
8
- from mindsdb.api.mongo.utilities.mongodb_query import MongoQuery
8
+ from mindsdb.integrations.handlers.mongodb_handler.utils.mongodb_query import MongoQuery
9
9
 
10
10
 
11
11
  class MongodbRender:
@@ -27,7 +27,7 @@ class MongodbRender:
27
27
  return self.select(node)
28
28
  elif isinstance(node, Update):
29
29
  return self.update(node)
30
- raise NotImplementedError(f'Unknown statement: {node.__class__.__name__}')
30
+ raise NotImplementedError(f"Unknown statement: {node.__class__.__name__}")
31
31
 
32
32
  def update(self, node: Update) -> MongoQuery:
33
33
  """
@@ -43,17 +43,8 @@ class MongodbRender:
43
43
  mquery = MongoQuery(collection)
44
44
 
45
45
  filters = self.handle_where(node.where)
46
- row = {
47
- k: v.value
48
- for k, v in node.update_columns.items()
49
- }
50
- mquery.add_step({
51
- 'method': 'update_many',
52
- 'args': [
53
- filters,
54
- {"$set": row}
55
- ]
56
- })
46
+ row = {k: v.value for k, v in node.update_columns.items()}
47
+ mquery.add_step({"method": "update_many", "args": [filters, {"$set": row}]})
57
48
  return mquery
58
49
 
59
50
  def select(self, node: Select):
@@ -67,7 +58,7 @@ class MongodbRender:
67
58
  MongoQuery: The converted MongoQuery instance.
68
59
  """
69
60
  if not isinstance(node.from_table, Identifier):
70
- raise NotImplementedError(f'Not supported from {node.from_table}')
61
+ raise NotImplementedError(f"Not supported from {node.from_table}")
71
62
 
72
63
  collection = node.from_table.parts[-1]
73
64
 
@@ -77,10 +68,10 @@ class MongodbRender:
77
68
  filters = self.handle_where(node.where)
78
69
 
79
70
  group = {}
80
- project = {'_id': 0} # Hide _id field when it has not been explicitly requested.
71
+ project = {"_id": 0} # Hide _id field when it has not been explicitly requested.
81
72
  if node.distinct:
82
73
  # Group by distinct fields.
83
- group = {'_id': {}}
74
+ group = {"_id": {}}
84
75
 
85
76
  if node.targets is not None:
86
77
  for col in node.targets:
@@ -95,12 +86,12 @@ class MongodbRender:
95
86
  else:
96
87
  alias = col.alias.parts[-1]
97
88
 
98
- project[alias] = f'${name}' # Project field.
89
+ project[alias] = f"${name}" # Project field.
99
90
 
100
91
  # Group by distinct fields.
101
92
  if node.distinct:
102
- group['_id'][name] = f'${name}' # Group field.
103
- group[name] = {'$first': f'${name}'} # Show field.
93
+ group["_id"][name] = f"${name}" # Group field.
94
+ group[name] = {"$first": f"${name}"} # Show field.
104
95
 
105
96
  elif isinstance(col, Constant):
106
97
  val = str(col.value) # Convert to string becuase it is interpreted as an index.
@@ -112,19 +103,19 @@ class MongodbRender:
112
103
 
113
104
  if node.group_by is not None:
114
105
  # TODO
115
- raise NotImplementedError(f'Group {node.group_by}')
106
+ raise NotImplementedError(f"Group {node.group_by}")
116
107
 
117
108
  sort = {}
118
109
  if node.order_by is not None:
119
110
  for col in node.order_by:
120
111
  name = col.field.parts[-1]
121
- direction = 1 if col.direction.upper() == 'ASC' else -1
112
+ direction = 1 if col.direction.upper() == "ASC" else -1
122
113
  sort[name] = direction
123
114
 
124
115
  # Compose the MongoDB query.
125
116
  mquery = MongoQuery(collection)
126
117
 
127
- method = 'aggregate'
118
+ method = "aggregate"
128
119
  arg = []
129
120
 
130
121
  # MongoDB related pipeline steps for the aggregate method.
@@ -150,10 +141,7 @@ class MongodbRender:
150
141
  if node.limit is not None:
151
142
  arg.append({"$limit": int(node.limit.value)})
152
143
 
153
- mquery.add_step({
154
- 'method': method,
155
- 'args': [arg]
156
- })
144
+ mquery.add_step({"method": method, "args": [arg]})
157
145
 
158
146
  return mquery
159
147
 
@@ -168,34 +156,34 @@ class MongodbRender:
168
156
  dict: The converted MongoDB query filters.
169
157
  """
170
158
  # TODO: UnaryOperation, function.
171
- if not type(node) in [BinaryOperation]:
172
- raise NotImplementedError(f'Not supported type {type(node)}')
159
+ if type(node) not in [BinaryOperation]:
160
+ raise NotImplementedError(f"Not supported type {type(node)}")
173
161
 
174
162
  op = node.op.lower()
175
163
  arg1, arg2 = node.args
176
164
 
177
- if op in ('and', 'or'):
165
+ if op in ("and", "or"):
178
166
  query1 = self.handle_where(arg1)
179
167
  query2 = self.handle_where(arg2)
180
168
 
181
169
  ops = {
182
- 'and': '$and',
183
- 'or': '$or',
170
+ "and": "$and",
171
+ "or": "$or",
184
172
  }
185
173
  query = {ops[op]: [query1, query2]}
186
174
  return query
187
175
 
188
176
  ops_map = {
189
- '>=': '$gte',
190
- '>': '$gt',
191
- '<': '$lt',
192
- '<=': '$lte',
193
- '<>': '$ne',
194
- '!=': '$ne',
195
- '=': '$eq',
196
- '==': '$eq',
197
- 'is': '$eq',
198
- 'is not': '$ne',
177
+ ">=": "$gte",
178
+ ">": "$gt",
179
+ "<": "$lt",
180
+ "<=": "$lte",
181
+ "<>": "$ne",
182
+ "!=": "$ne",
183
+ "=": "$eq",
184
+ "==": "$eq",
185
+ "is": "$eq",
186
+ "is not": "$ne",
199
187
  }
200
188
 
201
189
  if isinstance(arg1, Identifier):
@@ -203,35 +191,29 @@ class MongodbRender:
203
191
  # Simple operation.
204
192
  if isinstance(arg2, Constant):
205
193
  # Identifier and Constant.
206
- val = ObjectId(arg2.value) if var_name == '_id' else arg2.value
207
- if op in ('=', '=='):
194
+ val = ObjectId(arg2.value) if var_name == "_id" else arg2.value
195
+ if op in ("=", "=="):
208
196
  pass
209
197
  elif op in ops_map:
210
198
  op2 = ops_map[op]
211
199
  val = {op2: val}
212
200
  else:
213
- raise NotImplementedError(f'Not supported operator {op}')
201
+ raise NotImplementedError(f"Not supported operator {op}")
214
202
 
215
203
  return {var_name: val}
216
204
 
217
205
  # IN condition.
218
206
  elif isinstance(arg2, Tuple):
219
207
  # Should be IN, NOT IN.
220
- ops = {
221
- 'in': '$in',
222
- 'not in': '$nin'
223
- }
208
+ ops = {"in": "$in", "not in": "$nin"}
224
209
  # Must be list of Constants.
225
- values = [
226
- i.value
227
- for i in arg2.items
228
- ]
210
+ values = [i.value for i in arg2.items]
229
211
 
230
212
  if op in ops:
231
213
  op2 = ops[op]
232
214
  cond = {op2: values}
233
215
  else:
234
- raise NotImplementedError(f'Not supported operator {op}')
216
+ raise NotImplementedError(f"Not supported operator {op}")
235
217
 
236
218
  return {var_name: cond}
237
219
 
@@ -242,13 +224,9 @@ class MongodbRender:
242
224
  if op in ops_map:
243
225
  op2 = ops_map[op]
244
226
  else:
245
- raise NotImplementedError(f'Not supported operator {op}')
227
+ raise NotImplementedError(f"Not supported operator {op}")
246
228
 
247
- return {
248
- '$expr': {
249
- op2: [val1, val2]
250
- }
251
- }
229
+ return {"$expr": {op2: [val1, val2]}}
252
230
 
253
231
  def where_element_convert(self, node: Union[Identifier, Latest, Constant, TypeCast]) -> Any:
254
232
  """
@@ -265,22 +243,18 @@ class MongodbRender:
265
243
  RuntimeError: If the date format is not supported.
266
244
  """
267
245
  if isinstance(node, Identifier):
268
- return f'${node.parts[-1]}'
246
+ return f"${node.parts[-1]}"
269
247
  elif isinstance(node, Latest):
270
- return 'LATEST'
248
+ return "LATEST"
271
249
  elif isinstance(node, Constant):
272
250
  return node.value
273
- elif isinstance(node, TypeCast)\
274
- and node.type_name.upper() in ('DATE', 'DATETIME'):
275
- formats = [
276
- "%Y-%m-%d",
277
- "%Y-%m-%dT%H:%M:%S.%f"
278
- ]
251
+ elif isinstance(node, TypeCast) and node.type_name.upper() in ("DATE", "DATETIME"):
252
+ formats = ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S.%f"]
279
253
  for format in formats:
280
254
  try:
281
255
  return dt.datetime.strptime(node.arg.value, format)
282
256
  except ValueError:
283
257
  pass
284
- raise RuntimeError(f'Not supported date format. Supported: {formats}')
258
+ raise RuntimeError(f"Not supported date format. Supported: {formats}")
285
259
  else:
286
- raise NotImplementedError(f'Unknown where element {node}')
260
+ raise NotImplementedError(f"Unknown where element {node}")
@@ -58,7 +58,7 @@ class BaseHandler:
58
58
 
59
59
  Args:
60
60
  query (Any): query in native format (str for sql databases,
61
- dict for mongo, etc)
61
+ etc)
62
62
 
63
63
  Returns:
64
64
  HandlerResponse