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.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +5 -45
- mindsdb/api/a2a/__init__.py +52 -0
- mindsdb/api/a2a/agent.py +17 -28
- mindsdb/api/a2a/common/server/server.py +17 -36
- mindsdb/api/a2a/common/server/task_manager.py +14 -28
- mindsdb/api/a2a/common/types.py +3 -4
- mindsdb/api/a2a/task_manager.py +43 -55
- mindsdb/api/a2a/utils.py +63 -0
- mindsdb/api/common/middleware.py +106 -0
- mindsdb/api/http/initialize.py +13 -15
- mindsdb/api/http/namespaces/agents.py +6 -7
- mindsdb/api/http/namespaces/auth.py +6 -14
- mindsdb/api/http/namespaces/config.py +0 -2
- mindsdb/api/http/namespaces/default.py +74 -106
- mindsdb/api/http/start.py +25 -44
- mindsdb/api/litellm/start.py +11 -10
- mindsdb/api/mcp/__init__.py +165 -0
- mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +33 -64
- mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +86 -85
- mindsdb/integrations/handlers/crate_handler/crate_handler.py +3 -7
- mindsdb/integrations/handlers/derby_handler/derby_handler.py +32 -34
- mindsdb/integrations/handlers/documentdb_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/dummy_data_handler/dummy_data_handler.py +12 -13
- mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +45 -44
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +101 -95
- mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +129 -129
- mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +59 -43
- mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +38 -39
- mindsdb/integrations/handlers/informix_handler/informix_handler.py +5 -18
- mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +22 -28
- mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +3 -7
- mindsdb/integrations/handlers/mongodb_handler/mongodb_handler.py +53 -67
- mindsdb/integrations/handlers/mongodb_handler/requirements.txt +1 -0
- mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_ast.py +43 -68
- mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_parser.py +17 -25
- mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_query.py +10 -16
- mindsdb/integrations/handlers/mongodb_handler/utils/mongodb_render.py +43 -69
- mindsdb/integrations/libs/base.py +1 -1
- mindsdb/interfaces/agents/constants.py +17 -2
- mindsdb/interfaces/agents/langchain_agent.py +83 -18
- mindsdb/interfaces/knowledge_base/controller.py +3 -1
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +7 -1
- mindsdb/interfaces/skills/skill_tool.py +7 -1
- mindsdb/interfaces/skills/sql_agent.py +6 -2
- mindsdb/utilities/config.py +3 -155
- mindsdb/utilities/fs.py +10 -4
- mindsdb/utilities/log.py +0 -25
- mindsdb/utilities/starters.py +0 -39
- {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/METADATA +265 -263
- {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/RECORD +54 -98
- mindsdb/api/a2a/__main__.py +0 -144
- mindsdb/api/a2a/run_a2a.py +0 -86
- mindsdb/api/common/check_auth.py +0 -42
- mindsdb/api/http/gunicorn_wrapper.py +0 -17
- mindsdb/api/mcp/start.py +0 -205
- mindsdb/api/mongo/__init__.py +0 -0
- mindsdb/api/mongo/classes/__init__.py +0 -5
- mindsdb/api/mongo/classes/query_sql.py +0 -19
- mindsdb/api/mongo/classes/responder.py +0 -45
- mindsdb/api/mongo/classes/responder_collection.py +0 -34
- mindsdb/api/mongo/classes/scram.py +0 -86
- mindsdb/api/mongo/classes/session.py +0 -23
- mindsdb/api/mongo/functions/__init__.py +0 -19
- mindsdb/api/mongo/responders/__init__.py +0 -73
- mindsdb/api/mongo/responders/add_shard.py +0 -13
- mindsdb/api/mongo/responders/aggregate.py +0 -90
- mindsdb/api/mongo/responders/buildinfo.py +0 -17
- mindsdb/api/mongo/responders/coll_stats.py +0 -63
- mindsdb/api/mongo/responders/company_id.py +0 -25
- mindsdb/api/mongo/responders/connection_status.py +0 -22
- mindsdb/api/mongo/responders/count.py +0 -21
- mindsdb/api/mongo/responders/db_stats.py +0 -32
- mindsdb/api/mongo/responders/delete.py +0 -105
- mindsdb/api/mongo/responders/describe.py +0 -23
- mindsdb/api/mongo/responders/end_sessions.py +0 -13
- mindsdb/api/mongo/responders/find.py +0 -175
- mindsdb/api/mongo/responders/get_cmd_line_opts.py +0 -18
- mindsdb/api/mongo/responders/get_free_monitoring_status.py +0 -14
- mindsdb/api/mongo/responders/get_parameter.py +0 -23
- mindsdb/api/mongo/responders/getlog.py +0 -14
- mindsdb/api/mongo/responders/host_info.py +0 -28
- mindsdb/api/mongo/responders/insert.py +0 -270
- mindsdb/api/mongo/responders/is_master.py +0 -20
- mindsdb/api/mongo/responders/is_master_lower.py +0 -13
- mindsdb/api/mongo/responders/list_collections.py +0 -55
- mindsdb/api/mongo/responders/list_databases.py +0 -37
- mindsdb/api/mongo/responders/list_indexes.py +0 -22
- mindsdb/api/mongo/responders/ping.py +0 -13
- mindsdb/api/mongo/responders/recv_chunk_start.py +0 -13
- mindsdb/api/mongo/responders/replsetgetstatus.py +0 -13
- mindsdb/api/mongo/responders/sasl_continue.py +0 -34
- mindsdb/api/mongo/responders/sasl_start.py +0 -33
- mindsdb/api/mongo/responders/update_range_deletions.py +0 -12
- mindsdb/api/mongo/responders/whatsmyuri.py +0 -18
- mindsdb/api/mongo/server.py +0 -388
- mindsdb/api/mongo/start.py +0 -15
- mindsdb/api/mongo/utilities/__init__.py +0 -0
- {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/top_level.txt +0 -0
|
@@ -19,9 +19,10 @@ logger = log.getLogger(__name__)
|
|
|
19
19
|
|
|
20
20
|
class GoogleSearchConsoleHandler(APIHandler):
|
|
21
21
|
"""
|
|
22
|
-
|
|
22
|
+
A class for handling connections and interactions with the Google Search Console API.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
name = "google_search"
|
|
25
26
|
|
|
26
27
|
def __init__(self, name: str, **kwargs):
|
|
27
28
|
"""
|
|
@@ -33,19 +34,21 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
33
34
|
super().__init__(name)
|
|
34
35
|
self.token = None
|
|
35
36
|
self.service = None
|
|
36
|
-
self.connection_data = kwargs.get(
|
|
37
|
-
self.fs_storage = kwargs[
|
|
38
|
-
self.credentials_file = self.connection_data.get(
|
|
37
|
+
self.connection_data = kwargs.get("connection_data", {})
|
|
38
|
+
self.fs_storage = kwargs["file_storage"]
|
|
39
|
+
self.credentials_file = self.connection_data.get("credentials", None)
|
|
39
40
|
self.credentials = None
|
|
40
|
-
self.scopes = [
|
|
41
|
-
|
|
41
|
+
self.scopes = [
|
|
42
|
+
"https://www.googleapis.com/auth/webmasters.readonly",
|
|
43
|
+
"https://www.googleapis.com/auth/webmasters",
|
|
44
|
+
]
|
|
42
45
|
self.is_connected = False
|
|
43
46
|
analytics = SearchAnalyticsTable(self)
|
|
44
47
|
self.analytics = analytics
|
|
45
|
-
self._register_table(
|
|
48
|
+
self._register_table("Analytics", analytics)
|
|
46
49
|
sitemaps = SiteMapsTable(self)
|
|
47
50
|
self.sitemaps = sitemaps
|
|
48
|
-
self._register_table(
|
|
51
|
+
self._register_table("Sitemaps", sitemaps)
|
|
49
52
|
|
|
50
53
|
def connect(self):
|
|
51
54
|
"""
|
|
@@ -59,7 +62,7 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
59
62
|
return self.service
|
|
60
63
|
if self.credentials_file:
|
|
61
64
|
try:
|
|
62
|
-
json_str_bytes = self.fs_storage.file_get(
|
|
65
|
+
json_str_bytes = self.fs_storage.file_get("token_search.json")
|
|
63
66
|
json_str = json_str_bytes.decode()
|
|
64
67
|
self.credentials = Credentials.from_authorized_user_info(info=json.loads(json_str), scopes=self.scopes)
|
|
65
68
|
except Exception:
|
|
@@ -69,13 +72,12 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
69
72
|
if self.credentials and self.credentials.expired and self.credentials.refresh_token:
|
|
70
73
|
self.credentials.refresh(Request())
|
|
71
74
|
else:
|
|
72
|
-
self.credentials = Credentials.from_authorized_user_file(
|
|
73
|
-
self.credentials_file, scopes=self.scopes)
|
|
75
|
+
self.credentials = Credentials.from_authorized_user_file(self.credentials_file, scopes=self.scopes)
|
|
74
76
|
# Save the credentials for the next run
|
|
75
77
|
json_str = self.credentials.to_json()
|
|
76
|
-
self.fs_storage.file_set(
|
|
78
|
+
self.fs_storage.file_set("token_search.json", json_str.encode())
|
|
77
79
|
|
|
78
|
-
self.service = build(
|
|
80
|
+
self.service = build("webmasters", "v3", credentials=self.credentials)
|
|
79
81
|
return self.service
|
|
80
82
|
|
|
81
83
|
def check_connection(self) -> StatusResponse:
|
|
@@ -90,7 +92,7 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
90
92
|
self.connect()
|
|
91
93
|
response.success = True
|
|
92
94
|
except Exception as e:
|
|
93
|
-
logger.error(f
|
|
95
|
+
logger.error(f"Error connecting to Google Search Console API: {e}!")
|
|
94
96
|
response.error_message = e
|
|
95
97
|
|
|
96
98
|
self.is_connected = response.success
|
|
@@ -101,7 +103,7 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
101
103
|
Receive raw query and act upon it somehow.
|
|
102
104
|
Args:
|
|
103
105
|
query (Any): query in native format (str for sql databases,
|
|
104
|
-
|
|
106
|
+
api's json etc)
|
|
105
107
|
Returns:
|
|
106
108
|
HandlerResponse
|
|
107
109
|
"""
|
|
@@ -109,10 +111,7 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
109
111
|
|
|
110
112
|
df = self.call_application_api(method_name, params)
|
|
111
113
|
|
|
112
|
-
return Response(
|
|
113
|
-
RESPONSE_TYPE.TABLE,
|
|
114
|
-
data_frame=df
|
|
115
|
-
)
|
|
114
|
+
return Response(RESPONSE_TYPE.TABLE, data_frame=df)
|
|
116
115
|
|
|
117
116
|
def get_traffic_data(self, params: dict = None) -> DataFrame:
|
|
118
117
|
"""
|
|
@@ -123,14 +122,14 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
123
122
|
DataFrame
|
|
124
123
|
"""
|
|
125
124
|
service = self.connect()
|
|
126
|
-
accepted_params = [
|
|
125
|
+
accepted_params = ["start_date", "end_date", "dimensions", "row_limit", "aggregation_type"]
|
|
127
126
|
search_analytics_query_request = {
|
|
128
127
|
key: value for key, value in params.items() if key in accepted_params and value is not None
|
|
129
128
|
}
|
|
130
|
-
response =
|
|
131
|
-
query(siteUrl=params[
|
|
132
|
-
|
|
133
|
-
df = pd.DataFrame(response[
|
|
129
|
+
response = (
|
|
130
|
+
service.searchanalytics().query(siteUrl=params["siteUrl"], body=search_analytics_query_request).execute()
|
|
131
|
+
)
|
|
132
|
+
df = pd.DataFrame(response["rows"], columns=self.analytics.get_columns())
|
|
134
133
|
return df
|
|
135
134
|
|
|
136
135
|
def get_sitemaps(self, params: dict = None) -> DataFrame:
|
|
@@ -142,18 +141,18 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
142
141
|
DataFrame
|
|
143
142
|
"""
|
|
144
143
|
service = self.connect()
|
|
145
|
-
if params[
|
|
146
|
-
response = service.sitemaps().list(siteUrl=params[
|
|
144
|
+
if params["sitemapIndex"]:
|
|
145
|
+
response = service.sitemaps().list(siteUrl=params["siteUrl"], sitemapIndex=params["sitemapIndex"]).execute()
|
|
147
146
|
else:
|
|
148
|
-
response = service.sitemaps().list(siteUrl=params[
|
|
149
|
-
df = pd.DataFrame(response[
|
|
147
|
+
response = service.sitemaps().list(siteUrl=params["siteUrl"]).execute()
|
|
148
|
+
df = pd.DataFrame(response["sitemap"], columns=self.sitemaps.get_columns())
|
|
150
149
|
|
|
151
150
|
# Get as many sitemaps as indicated by the row_limit parameter
|
|
152
|
-
if params[
|
|
153
|
-
if params[
|
|
151
|
+
if params["row_limit"]:
|
|
152
|
+
if params["row_limit"] > len(df):
|
|
154
153
|
row_limit = len(df)
|
|
155
154
|
else:
|
|
156
|
-
row_limit = params[
|
|
155
|
+
row_limit = params["row_limit"]
|
|
157
156
|
|
|
158
157
|
df = df[:row_limit]
|
|
159
158
|
|
|
@@ -168,7 +167,7 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
168
167
|
DataFrame
|
|
169
168
|
"""
|
|
170
169
|
service = self.connect()
|
|
171
|
-
response = service.sitemaps().submit(siteUrl=params[
|
|
170
|
+
response = service.sitemaps().submit(siteUrl=params["siteUrl"], feedpath=params["feedpath"]).execute()
|
|
172
171
|
df = pd.DataFrame(response, columns=self.sitemaps.get_columns())
|
|
173
172
|
return df
|
|
174
173
|
|
|
@@ -181,7 +180,7 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
181
180
|
DataFrame
|
|
182
181
|
"""
|
|
183
182
|
service = self.connect()
|
|
184
|
-
response = service.sitemaps().delete(siteUrl=params[
|
|
183
|
+
response = service.sitemaps().delete(siteUrl=params["siteUrl"], feedpath=params["feedpath"]).execute()
|
|
185
184
|
df = pd.DataFrame(response, columns=self.sitemaps.get_columns())
|
|
186
185
|
return df
|
|
187
186
|
|
|
@@ -194,13 +193,13 @@ class GoogleSearchConsoleHandler(APIHandler):
|
|
|
194
193
|
Returns:
|
|
195
194
|
DataFrame
|
|
196
195
|
"""
|
|
197
|
-
if method_name ==
|
|
196
|
+
if method_name == "get_traffic_data":
|
|
198
197
|
return self.get_traffic_data(params)
|
|
199
|
-
elif method_name ==
|
|
198
|
+
elif method_name == "get_sitemaps":
|
|
200
199
|
return self.get_sitemaps(params)
|
|
201
|
-
elif method_name ==
|
|
200
|
+
elif method_name == "submit_sitemap":
|
|
202
201
|
return self.submit_sitemap(params)
|
|
203
|
-
elif method_name ==
|
|
202
|
+
elif method_name == "delete_sitemap":
|
|
204
203
|
return self.delete_sitemap(params)
|
|
205
204
|
else:
|
|
206
|
-
raise NotImplementedError(f
|
|
205
|
+
raise NotImplementedError(f"Unknown method {method_name}")
|
|
@@ -21,7 +21,6 @@ logger = log.getLogger(__name__)
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class InformixHandler(DatabaseHandler):
|
|
24
|
-
|
|
25
24
|
name = "informix"
|
|
26
25
|
|
|
27
26
|
def __init__(self, name: str, connection_data: Optional[dict], **kwargs):
|
|
@@ -35,11 +34,7 @@ class InformixHandler(DatabaseHandler):
|
|
|
35
34
|
|
|
36
35
|
self.kwargs = kwargs
|
|
37
36
|
self.parser = parse_sql
|
|
38
|
-
self.loging_enabled =
|
|
39
|
-
connection_data["loging_enabled"]
|
|
40
|
-
if "loging_enabled" in connection_data
|
|
41
|
-
else True
|
|
42
|
-
)
|
|
37
|
+
self.loging_enabled = connection_data["loging_enabled"] if "loging_enabled" in connection_data else True
|
|
43
38
|
self.server = connection_data["server"]
|
|
44
39
|
self.database = connection_data["database"]
|
|
45
40
|
self.user = connection_data["user"]
|
|
@@ -47,9 +42,7 @@ class InformixHandler(DatabaseHandler):
|
|
|
47
42
|
self.schemaName = connection_data["schema_name"]
|
|
48
43
|
self.host = connection_data["host"]
|
|
49
44
|
self.port = connection_data["port"]
|
|
50
|
-
self.connString = (
|
|
51
|
-
"SERVER={0};" "DATABASE={1};" "HOST={2};" "PORT={3};" "UID={4};" "PWD={5};"
|
|
52
|
-
).format(
|
|
45
|
+
self.connString = ("SERVER={0};DATABASE={1};HOST={2};PORT={3};UID={4};PWD={5};").format(
|
|
53
46
|
self.server, self.database, self.host, self.port, self.user, self.password
|
|
54
47
|
)
|
|
55
48
|
|
|
@@ -115,7 +108,7 @@ class InformixHandler(DatabaseHandler):
|
|
|
115
108
|
"""Receive raw query and act upon it somehow.
|
|
116
109
|
Args:
|
|
117
110
|
query (Any): query in native format (str for sql databases,
|
|
118
|
-
|
|
111
|
+
etc)
|
|
119
112
|
Returns:
|
|
120
113
|
HandlerResponse
|
|
121
114
|
"""
|
|
@@ -129,9 +122,7 @@ class InformixHandler(DatabaseHandler):
|
|
|
129
122
|
result = cur.fetchall()
|
|
130
123
|
response = Response(
|
|
131
124
|
RESPONSE_TYPE.TABLE,
|
|
132
|
-
data_frame=pd.DataFrame(
|
|
133
|
-
result, columns=[x[0] for x in cur.description]
|
|
134
|
-
),
|
|
125
|
+
data_frame=pd.DataFrame(result, columns=[x[0] for x in cur.description]),
|
|
135
126
|
)
|
|
136
127
|
else:
|
|
137
128
|
response = Response(RESPONSE_TYPE.OK)
|
|
@@ -179,11 +170,7 @@ class InformixHandler(DatabaseHandler):
|
|
|
179
170
|
response = Response(
|
|
180
171
|
RESPONSE_TYPE.TABLE,
|
|
181
172
|
data_frame=pd.DataFrame(
|
|
182
|
-
[
|
|
183
|
-
x["TABLE_NAME"]
|
|
184
|
-
for x in result
|
|
185
|
-
if x["TABLE_SCHEM"] == self.schemaName
|
|
186
|
-
],
|
|
173
|
+
[x["TABLE_NAME"] for x in result if x["TABLE_SCHEM"] == self.schemaName],
|
|
187
174
|
columns=["TABLE_NAME"],
|
|
188
175
|
),
|
|
189
176
|
)
|
|
@@ -9,7 +9,8 @@ from mindsdb.utilities import log
|
|
|
9
9
|
from mindsdb.integrations.libs.response import (
|
|
10
10
|
HandlerStatusResponse as StatusResponse,
|
|
11
11
|
HandlerResponse as Response,
|
|
12
|
-
RESPONSE_TYPE,
|
|
12
|
+
RESPONSE_TYPE,
|
|
13
|
+
HandlerResponse,
|
|
13
14
|
)
|
|
14
15
|
import pandas as pd
|
|
15
16
|
import jaydebeapi as jd
|
|
@@ -19,13 +20,13 @@ logger = log.getLogger(__name__)
|
|
|
19
20
|
|
|
20
21
|
class MaxDBHandler(DatabaseHandler):
|
|
21
22
|
"""
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
This handler handles connection and execution of the SAP MaxDB statements.
|
|
24
|
+
"""
|
|
24
25
|
|
|
25
|
-
name =
|
|
26
|
+
name = "maxdb"
|
|
26
27
|
|
|
27
28
|
def __init__(self, name: str, connection_data: Optional[dict], **kwargs):
|
|
28
|
-
"""
|
|
29
|
+
"""Initialize the handler
|
|
29
30
|
Args:
|
|
30
31
|
name (str): name of particular handler instance
|
|
31
32
|
connection_data (dict): parameters for connecting to the database
|
|
@@ -35,12 +36,12 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
35
36
|
self.kwargs = kwargs
|
|
36
37
|
self.parser = parse_sql
|
|
37
38
|
self.connection_config = connection_data
|
|
38
|
-
self.database = connection_data[
|
|
39
|
-
self.host = connection_data[
|
|
40
|
-
self.port = connection_data[
|
|
41
|
-
self.user = connection_data[
|
|
42
|
-
self.password = connection_data[
|
|
43
|
-
self.jdbc_location = connection_data[
|
|
39
|
+
self.database = connection_data["database"]
|
|
40
|
+
self.host = connection_data["host"]
|
|
41
|
+
self.port = connection_data["port"]
|
|
42
|
+
self.user = connection_data["user"]
|
|
43
|
+
self.password = connection_data["password"]
|
|
44
|
+
self.jdbc_location = connection_data["jdbc_location"]
|
|
44
45
|
self.connection = None
|
|
45
46
|
self.is_connected = False
|
|
46
47
|
|
|
@@ -61,14 +62,14 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
61
62
|
return self.connection
|
|
62
63
|
|
|
63
64
|
jdbc_url = f"jdbc:sapdb://{self.host}:{self.port}/{self.database}"
|
|
64
|
-
jdbc_class =
|
|
65
|
+
jdbc_class = "com.sap.dbtech.jdbc.DriverSapDB"
|
|
65
66
|
|
|
66
67
|
self.connection = jd.connect(jdbc_class, jdbc_url, [self.user, self.password], self.jdbc_location)
|
|
67
68
|
self.is_connected = True
|
|
68
69
|
return self.connection
|
|
69
70
|
|
|
70
71
|
def disconnect(self):
|
|
71
|
-
"""
|
|
72
|
+
"""Close any existing connections
|
|
72
73
|
Should switch self.is_connected.
|
|
73
74
|
"""
|
|
74
75
|
if self.is_connected is False:
|
|
@@ -82,7 +83,7 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
82
83
|
return
|
|
83
84
|
|
|
84
85
|
def check_connection(self) -> StatusResponse:
|
|
85
|
-
"""
|
|
86
|
+
"""Check connection to the handler
|
|
86
87
|
Returns:
|
|
87
88
|
HandlerStatusResponse
|
|
88
89
|
"""
|
|
@@ -93,7 +94,7 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
93
94
|
self.connect()
|
|
94
95
|
response.success = True
|
|
95
96
|
except Exception as e:
|
|
96
|
-
logger.error(f
|
|
97
|
+
logger.error(f"Error connecting to database {self.database}, {e}!")
|
|
97
98
|
response.error_message = str(e)
|
|
98
99
|
finally:
|
|
99
100
|
if response.success is True and need_to_close:
|
|
@@ -107,7 +108,7 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
107
108
|
"""Receive raw query and act upon it somehow.
|
|
108
109
|
Args:
|
|
109
110
|
query (Any): query in native format (str for sql databases,
|
|
110
|
-
|
|
111
|
+
etc)
|
|
111
112
|
Returns:
|
|
112
113
|
HandlerResponse
|
|
113
114
|
"""
|
|
@@ -119,21 +120,14 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
119
120
|
if cur.description:
|
|
120
121
|
result = cur.fetchall()
|
|
121
122
|
response = Response(
|
|
122
|
-
RESPONSE_TYPE.TABLE,
|
|
123
|
-
data_frame=pd.DataFrame(
|
|
124
|
-
result,
|
|
125
|
-
columns=[x[0] for x in cur.description]
|
|
126
|
-
)
|
|
123
|
+
RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame(result, columns=[x[0] for x in cur.description])
|
|
127
124
|
)
|
|
128
125
|
else:
|
|
129
126
|
response = Response(RESPONSE_TYPE.OK)
|
|
130
127
|
self.connection.commit()
|
|
131
128
|
except Exception as e:
|
|
132
|
-
logger.error(f
|
|
133
|
-
response = Response(
|
|
134
|
-
RESPONSE_TYPE.ERROR,
|
|
135
|
-
error_message=str(e)
|
|
136
|
-
)
|
|
129
|
+
logger.error(f"Error running query: {query} on {self.database}!")
|
|
130
|
+
response = Response(RESPONSE_TYPE.ERROR, error_message=str(e))
|
|
137
131
|
self.connection.rollback()
|
|
138
132
|
|
|
139
133
|
if need_to_close is True:
|
|
@@ -165,7 +159,7 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
165
159
|
query = f"SELECT TABLENAME FROM DOMAIN.TABLES WHERE TYPE = 'TABLE' AND SCHEMANAME = '{self.user}'"
|
|
166
160
|
result = self.native_query(query)
|
|
167
161
|
df = result.data_frame
|
|
168
|
-
result.data_frame = df.rename(columns={df.columns[0]:
|
|
162
|
+
result.data_frame = df.rename(columns={df.columns[0]: "table_name"})
|
|
169
163
|
return result
|
|
170
164
|
|
|
171
165
|
def get_columns(self, table_name: str) -> Response:
|
|
@@ -182,5 +176,5 @@ class MaxDBHandler(DatabaseHandler):
|
|
|
182
176
|
query = f"SELECT COLUMNNAME,DATATYPE FROM DOMAIN.COLUMNS WHERE TABLENAME ='{table_name}'"
|
|
183
177
|
result = self.native_query(query)
|
|
184
178
|
df = result.data_frame
|
|
185
|
-
result.data_frame = df.rename(columns={
|
|
179
|
+
result.data_frame = df.rename(columns={"name": "column_name", "type": "data_type"})
|
|
186
180
|
return self.native_query(query)
|
|
@@ -35,9 +35,7 @@ class MonetDBHandler(DatabaseHandler):
|
|
|
35
35
|
self.database = connection_data["database"]
|
|
36
36
|
self.user = connection_data["user"]
|
|
37
37
|
self.password = connection_data["password"]
|
|
38
|
-
self.schemaName =
|
|
39
|
-
connection_data["schema_name"] if "schema_name" in connection_data else None
|
|
40
|
-
)
|
|
38
|
+
self.schemaName = connection_data["schema_name"] if "schema_name" in connection_data else None
|
|
41
39
|
self.host = connection_data["host"]
|
|
42
40
|
self.port = connection_data["port"]
|
|
43
41
|
|
|
@@ -109,7 +107,7 @@ class MonetDBHandler(DatabaseHandler):
|
|
|
109
107
|
"""Receive raw query and act upon it somehow.
|
|
110
108
|
Args:
|
|
111
109
|
query (Any): query in native format (str for sql databases,
|
|
112
|
-
|
|
110
|
+
etc)
|
|
113
111
|
Returns:
|
|
114
112
|
HandlerResponse
|
|
115
113
|
"""
|
|
@@ -123,9 +121,7 @@ class MonetDBHandler(DatabaseHandler):
|
|
|
123
121
|
result = cur.fetchall()
|
|
124
122
|
response = Response(
|
|
125
123
|
RESPONSE_TYPE.TABLE,
|
|
126
|
-
data_frame=pd.DataFrame(
|
|
127
|
-
result, columns=[x[0] for x in cur.description]
|
|
128
|
-
),
|
|
124
|
+
data_frame=pd.DataFrame(result, columns=[x[0] for x in cur.description]),
|
|
129
125
|
)
|
|
130
126
|
else:
|
|
131
127
|
response = Response(RESPONSE_TYPE.OK)
|
|
@@ -10,13 +10,13 @@ from pymongo import MongoClient
|
|
|
10
10
|
from pymongo.errors import ServerSelectionTimeoutError, OperationFailure, ConfigurationError, InvalidURI
|
|
11
11
|
from typing import Text, List, Dict, Any, Union
|
|
12
12
|
|
|
13
|
-
from mindsdb.
|
|
14
|
-
from mindsdb.
|
|
13
|
+
from mindsdb.integrations.handlers.mongodb_handler.utils.mongodb_query import MongoQuery
|
|
14
|
+
from mindsdb.integrations.handlers.mongodb_handler.utils.mongodb_parser import MongodbParser
|
|
15
15
|
from mindsdb.integrations.libs.base import DatabaseHandler
|
|
16
16
|
from mindsdb.integrations.libs.response import (
|
|
17
17
|
HandlerStatusResponse as StatusResponse,
|
|
18
18
|
HandlerResponse as Response,
|
|
19
|
-
RESPONSE_TYPE
|
|
19
|
+
RESPONSE_TYPE,
|
|
20
20
|
)
|
|
21
21
|
from mindsdb.utilities import log
|
|
22
22
|
from .utils.mongodb_render import MongodbRender
|
|
@@ -31,7 +31,7 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
_SUBSCRIBE_SLEEP_INTERVAL = 0.5
|
|
34
|
-
name =
|
|
34
|
+
name = "mongodb"
|
|
35
35
|
|
|
36
36
|
def __init__(self, name: Text, **kwargs: Any) -> None:
|
|
37
37
|
"""
|
|
@@ -42,13 +42,13 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
42
42
|
kwargs: Arbitrary keyword arguments including the connection data.
|
|
43
43
|
"""
|
|
44
44
|
super().__init__(name)
|
|
45
|
-
connection_data = kwargs[
|
|
45
|
+
connection_data = kwargs["connection_data"]
|
|
46
46
|
self.host = connection_data.get("host")
|
|
47
47
|
self.port = int(connection_data.get("port") or 27017)
|
|
48
48
|
self.user = connection_data.get("username")
|
|
49
49
|
self.password = connection_data.get("password")
|
|
50
|
-
self.database = connection_data.get(
|
|
51
|
-
self.flatten_level = connection_data.get(
|
|
50
|
+
self.database = connection_data.get("database")
|
|
51
|
+
self.flatten_level = connection_data.get("flatten_level", 0)
|
|
52
52
|
|
|
53
53
|
self.connection = None
|
|
54
54
|
self.is_connected = False
|
|
@@ -72,31 +72,27 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
72
72
|
"""
|
|
73
73
|
kwargs = {}
|
|
74
74
|
if isinstance(self.user, str) and len(self.user) > 0:
|
|
75
|
-
kwargs[
|
|
75
|
+
kwargs["username"] = self.user
|
|
76
76
|
|
|
77
77
|
if isinstance(self.password, str) and len(self.password) > 0:
|
|
78
|
-
kwargs[
|
|
78
|
+
kwargs["password"] = self.password
|
|
79
79
|
|
|
80
|
-
if re.match(r
|
|
81
|
-
kwargs[
|
|
80
|
+
if re.match(r"/?.*tls=true", self.host.lower()):
|
|
81
|
+
kwargs["tls"] = True
|
|
82
82
|
|
|
83
|
-
if re.match(r
|
|
84
|
-
kwargs[
|
|
83
|
+
if re.match(r"/?.*tls=false", self.host.lower()):
|
|
84
|
+
kwargs["tls"] = False
|
|
85
85
|
|
|
86
86
|
try:
|
|
87
|
-
connection = MongoClient(
|
|
88
|
-
self.host,
|
|
89
|
-
port=self.port,
|
|
90
|
-
**kwargs
|
|
91
|
-
)
|
|
87
|
+
connection = MongoClient(self.host, port=self.port, **kwargs)
|
|
92
88
|
except InvalidURI as invalid_uri_error:
|
|
93
|
-
logger.error(f
|
|
89
|
+
logger.error(f"Invalid URI provided for MongoDB connection: {invalid_uri_error}!")
|
|
94
90
|
raise
|
|
95
91
|
except ConfigurationError as config_error:
|
|
96
|
-
logger.error(f
|
|
92
|
+
logger.error(f"Configuration error connecting to MongoDB: {config_error}!")
|
|
97
93
|
raise
|
|
98
94
|
except Exception as unknown_error:
|
|
99
|
-
logger.error(f
|
|
95
|
+
logger.error(f"Unknown error connecting to MongoDB: {unknown_error}!")
|
|
100
96
|
raise
|
|
101
97
|
|
|
102
98
|
# Get the database name from the connection if it's not provided.
|
|
@@ -107,7 +103,9 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
107
103
|
self.connection = connection
|
|
108
104
|
return self.connection
|
|
109
105
|
|
|
110
|
-
def subscribe(
|
|
106
|
+
def subscribe(
|
|
107
|
+
self, stop_event: threading.Event, callback: callable, table_name: Text, columns: List = None, **kwargs: Any
|
|
108
|
+
) -> None:
|
|
111
109
|
"""
|
|
112
110
|
Subscribes to changes in a MongoDB collection and calls the provided callback function when changes occur.
|
|
113
111
|
|
|
@@ -131,26 +129,26 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
131
129
|
time.sleep(self._SUBSCRIBE_SLEEP_INTERVAL)
|
|
132
130
|
continue
|
|
133
131
|
|
|
134
|
-
_id = res[
|
|
135
|
-
if res[
|
|
132
|
+
_id = res["documentKey"]["_id"]
|
|
133
|
+
if res["operationType"] == "insert":
|
|
136
134
|
if columns is not None:
|
|
137
|
-
updated_columns = set(res[
|
|
135
|
+
updated_columns = set(res["fullDocument"].keys())
|
|
138
136
|
if not set(columns) & set(updated_columns):
|
|
139
137
|
# Do nothing.
|
|
140
138
|
continue
|
|
141
139
|
|
|
142
|
-
callback(row=res[
|
|
140
|
+
callback(row=res["fullDocument"], key={"_id": _id})
|
|
143
141
|
|
|
144
|
-
if res[
|
|
142
|
+
if res["operationType"] == "update":
|
|
145
143
|
if columns is not None:
|
|
146
|
-
updated_columns = set(res[
|
|
144
|
+
updated_columns = set(res["updateDescription"]["updatedFields"].keys())
|
|
147
145
|
if not set(columns) & set(updated_columns):
|
|
148
146
|
# Do nothing.
|
|
149
147
|
continue
|
|
150
148
|
|
|
151
149
|
# Get the full document.
|
|
152
|
-
full_doc = con[self.database][table_name].find_one(res[
|
|
153
|
-
callback(row=full_doc, key={
|
|
150
|
+
full_doc = con[self.database][table_name].find_one(res["documentKey"])
|
|
151
|
+
callback(row=full_doc, key={"_id": _id})
|
|
154
152
|
|
|
155
153
|
def disconnect(self) -> None:
|
|
156
154
|
"""
|
|
@@ -178,14 +176,20 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
178
176
|
|
|
179
177
|
# Check if the database exists.
|
|
180
178
|
if self.database not in con.list_database_names():
|
|
181
|
-
raise ValueError(f
|
|
179
|
+
raise ValueError(f"Database {self.database} not found!")
|
|
182
180
|
|
|
183
181
|
response.success = True
|
|
184
|
-
except (
|
|
185
|
-
|
|
182
|
+
except (
|
|
183
|
+
InvalidURI,
|
|
184
|
+
ServerSelectionTimeoutError,
|
|
185
|
+
OperationFailure,
|
|
186
|
+
ConfigurationError,
|
|
187
|
+
ValueError,
|
|
188
|
+
) as known_error:
|
|
189
|
+
logger.error(f"Error connecting to MongoDB {self.database}, {known_error}!")
|
|
186
190
|
response.error_message = str(known_error)
|
|
187
191
|
except Exception as unknown_error:
|
|
188
|
-
logger.error(f
|
|
192
|
+
logger.error(f"Unknown error connecting to MongoDB {self.database}, {unknown_error}!")
|
|
189
193
|
response.error_message = str(unknown_error)
|
|
190
194
|
|
|
191
195
|
if response.success and need_to_close:
|
|
@@ -211,13 +215,10 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
211
215
|
|
|
212
216
|
if isinstance(query, dict):
|
|
213
217
|
# Fallback for the previous API.
|
|
214
|
-
mquery = MongoQuery(query[
|
|
218
|
+
mquery = MongoQuery(query["collection"])
|
|
215
219
|
|
|
216
|
-
for c in query[
|
|
217
|
-
mquery.add_step({
|
|
218
|
-
'method': c['method'],
|
|
219
|
-
'args': c['args']
|
|
220
|
-
})
|
|
220
|
+
for c in query["call"]:
|
|
221
|
+
mquery.add_step({"method": c["method"], "args": c["args"]})
|
|
221
222
|
|
|
222
223
|
query = mquery
|
|
223
224
|
|
|
@@ -229,16 +230,15 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
229
230
|
# Check if the collection exists.
|
|
230
231
|
if collection not in con[database].list_collection_names():
|
|
231
232
|
return Response(
|
|
232
|
-
RESPONSE_TYPE.ERROR,
|
|
233
|
-
error_message=f'Collection {collection} not found in database {database}!'
|
|
233
|
+
RESPONSE_TYPE.ERROR, error_message=f"Collection {collection} not found in database {database}!"
|
|
234
234
|
)
|
|
235
235
|
|
|
236
236
|
try:
|
|
237
237
|
cursor = con[database][collection]
|
|
238
238
|
|
|
239
239
|
for step in query.pipeline:
|
|
240
|
-
fnc = getattr(cursor, step[
|
|
241
|
-
cursor = fnc(*step[
|
|
240
|
+
fnc = getattr(cursor, step["method"])
|
|
241
|
+
cursor = fnc(*step["args"])
|
|
242
242
|
|
|
243
243
|
result = []
|
|
244
244
|
if not isinstance(cursor, pymongo.results.UpdateResult):
|
|
@@ -254,16 +254,10 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
254
254
|
columns = list(self.get_columns(collection).data_frame.Field)
|
|
255
255
|
df = pd.DataFrame([], columns=columns)
|
|
256
256
|
|
|
257
|
-
response = Response(
|
|
258
|
-
RESPONSE_TYPE.TABLE,
|
|
259
|
-
df
|
|
260
|
-
)
|
|
257
|
+
response = Response(RESPONSE_TYPE.TABLE, df)
|
|
261
258
|
except Exception as e:
|
|
262
|
-
logger.error(f
|
|
263
|
-
response = Response(
|
|
264
|
-
RESPONSE_TYPE.ERROR,
|
|
265
|
-
error_message=str(e)
|
|
266
|
-
)
|
|
259
|
+
logger.error(f"Error running query: {query} on {self.database}.{collection}!")
|
|
260
|
+
response = Response(RESPONSE_TYPE.ERROR, error_message=str(e))
|
|
267
261
|
|
|
268
262
|
return response
|
|
269
263
|
|
|
@@ -289,7 +283,7 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
289
283
|
if level > 0:
|
|
290
284
|
if isinstance(v, dict):
|
|
291
285
|
for k2, v2 in self.flatten(v, level=level - 1).items():
|
|
292
|
-
add[f
|
|
286
|
+
add[f"{k}.{k2}"] = v2
|
|
293
287
|
del_keys.append(k)
|
|
294
288
|
|
|
295
289
|
if add:
|
|
@@ -324,15 +318,10 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
324
318
|
"""
|
|
325
319
|
con = self.connect()
|
|
326
320
|
collections = con[self.database].list_collection_names()
|
|
327
|
-
collections_ar = [
|
|
328
|
-
|
|
329
|
-
]
|
|
330
|
-
df = pd.DataFrame(collections_ar, columns=['table_name'])
|
|
321
|
+
collections_ar = [[i] for i in collections]
|
|
322
|
+
df = pd.DataFrame(collections_ar, columns=["table_name"])
|
|
331
323
|
|
|
332
|
-
response = Response(
|
|
333
|
-
RESPONSE_TYPE.TABLE,
|
|
334
|
-
df
|
|
335
|
-
)
|
|
324
|
+
response = Response(RESPONSE_TYPE.TABLE, df)
|
|
336
325
|
|
|
337
326
|
return response
|
|
338
327
|
|
|
@@ -363,10 +352,7 @@ class MongoDBHandler(DatabaseHandler):
|
|
|
363
352
|
for k, v in record.items():
|
|
364
353
|
data.append([k, type(v).__name__])
|
|
365
354
|
|
|
366
|
-
df = pd.DataFrame(data, columns=[
|
|
355
|
+
df = pd.DataFrame(data, columns=["Field", "Type"])
|
|
367
356
|
|
|
368
|
-
response = Response(
|
|
369
|
-
RESPONSE_TYPE.TABLE,
|
|
370
|
-
df
|
|
371
|
-
)
|
|
357
|
+
response = Response(RESPONSE_TYPE.TABLE, df)
|
|
372
358
|
return response
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pymongo == 4.8.0
|