-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MongoDB/PyMongo: Add querying capabilities using JessiQL
With corresponding improvements, the amalgamated PyMongo driver can now run 95% of the MongoDB "getting started" tutorial successfully.
- Loading branch information
Showing
12 changed files
with
471 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,26 @@ | ||
# PyMongo CrateDB Adapter | ||
# PyMongo CrateDB Adapter Backlog | ||
|
||
|
||
## Iteration +1 | ||
|
||
Make it work. | ||
- Translate query expressions. | ||
|
||
- Using individual columns for fields does not work, because `insert_one` works | ||
iteratively, and needs to evolve the table schema gradually. As a consequence, | ||
we need to use `OBJECT(DYNAMIC)` for storing MongoDB fields. | ||
- Add software tests | ||
- Add documentation | ||
- https://github.com/gordonbusyman/mongo-to-sql-converter | ||
- https://github.com/2do2go/json-sql | ||
|
||
## Iteration +2 | ||
- https://github.com/kolypto/py-mongosql | ||
- https://github.com/SY-Xuan/mongo2sql | ||
- https://github.com/nsragow/MongoToSqlParse | ||
- https://github.com/sushmitharao2124/MongoToSQLConverter | ||
- https://github.com/Phomint/MongoSQL | ||
|
||
- Add documentation | ||
|
||
Translate query expressions. | ||
|
||
- https://github.com/gordonbusyman/mongo-to-sql-converter | ||
- https://github.com/2do2go/json-sql | ||
## Done | ||
|
||
- https://github.com/SY-Xuan/mongo2sql | ||
- https://github.com/nsragow/MongoToSqlParse | ||
- https://github.com/sushmitharao2124/MongoToSQLConverter | ||
- https://github.com/Phomint/MongoSQL | ||
- Make it work | ||
- Using individual columns for fields does not work, because `insert_one` works | ||
iteratively, and needs to evolve the table schema gradually. As a consequence, | ||
we need to use `OBJECT(DYNAMIC)` for storing MongoDB fields. | ||
- Add software tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import typing as t | ||
|
||
import sqlalchemy as sa | ||
from jessiql import Query, QueryObject | ||
from jessiql.exc import InvalidColumnError | ||
from jessiql.typing import SARowDict | ||
|
||
|
||
def table_to_model(table: sa.Table) -> t.Type[sa.orm.Mapper]: | ||
""" | ||
Create SQLAlchemy model class from Table object. | ||
- https://docs.sqlalchemy.org/en/14/orm/mapping_styles.html#imperative-mapping | ||
- https://sparrigan.github.io/sql/sqla/2016/01/03/dynamic-tables.html | ||
""" | ||
mapper_registry = sa.orm.registry(metadata=table.metadata) | ||
Surrogate = type("Surrogate", (), {}) | ||
mapper_registry.map_imperatively(Surrogate, table) | ||
return Surrogate | ||
|
||
|
||
def reflect_model(engine: t.Any, metadata: sa.MetaData, table_name: str) -> t.Type[sa.orm.Mapper]: | ||
""" | ||
Create SQLAlchemy model class by reflecting a database table. | ||
""" | ||
table = sa.Table(table_name, metadata, autoload_with=engine) | ||
return table_to_model(table) | ||
|
||
|
||
def mongodb_query( | ||
model: t.Type[sa.orm.Mapper], | ||
select: t.List = None, | ||
filter: t.Dict = None, # noqa: A002 | ||
sort: t.Dict = None, | ||
) -> Query: | ||
""" | ||
Create a JessiQL Query object from an SQLAlchemy model class and typical MongoDB query parameters. | ||
""" | ||
|
||
select = select or list(model._sa_class_manager.keys()) | ||
|
||
filter = filter or {} # noqa: A001 | ||
sort = sort or [] | ||
|
||
# TODO: select, filter, sort, skip, limit | ||
if "_id" in filter: | ||
filter["_id"] = str(filter["_id"]) | ||
query_dict = {"select": select, "filter": filter, "sort": sort} | ||
query_object = QueryObject.from_query_object(query_dict) | ||
|
||
try: | ||
return Query(query=query_object, Model=model) | ||
except InvalidColumnError as ex: | ||
msg = str(ex) | ||
if "Invalid column" in msg and "specified in filter" in msg: | ||
return EmptyQuery() | ||
else: | ||
raise | ||
|
||
|
||
class EmptyQuery(Query): | ||
""" | ||
A surrogate QueryExecutor for propagating back empty results. | ||
""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.related_executors = {} | ||
|
||
def _load_results(self, *args, **kwargs) -> t.List[SARowDict]: | ||
return [] | ||
|
||
def _apply_operations_to_results(self, *args, **kwargs) -> t.List[SARowDict]: | ||
return [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import datetime as dt | ||
import math | ||
|
||
|
||
def truncate_milliseconds(timestamp: dt.datetime): | ||
""" | ||
Downgrade Python datetime objects from microseconds to milliseconds resolution. | ||
Input: 2023-11-26 21:57:18.537624 | ||
Output: 2023-11-26 21:57:18.537000 | ||
""" | ||
dec, integer = math.modf(timestamp.microsecond / 1000) | ||
return timestamp - dt.timedelta(microseconds=round(dec * 1000)) |
Oops, something went wrong.