I'm not at a computer right now but what you need to do is write a simple
recursive descent parser, which makes use of the objects in
sqlalchemy.sql.operators for the operators as it parses the string into an
expression tree.  This is actually a fun classic computer problem I'm sure
someone can show here or I can demo one later.

On Fri, Mar 29, 2019, 2:31 PM Ian Miller <irmille...@gmail.com> wrote:

> The code that makes up the query builder I've been working on is pretty
> extensive, so I'll go through the high-level basics.
>
> I've set up a `_base` method that augments the SQLALchemy Base model. Note
> the column details retrived in `get_column_and_json_key_from_sql_name` class
> method:
>
> class _base:
>     """
>     This class augments the default SQLAlchemy Base model
>     """
>
>     @classmethod
>     def get_column_and_json_key_from_sql_name(cls, name):
>         """
>         Returns the column and, if applicable, the JSON top-level key from 
> the JSON dict.
>
>         :param name: Name of field.
>
>         :return:
>         """
>         assert type(name) == str
>
>         json_key = None
>         col_names = name.split(COLUMN_NAME_DELIMITER)
>         if len(col_names) == 2:
>             name, json_key = col_names
>
>         try:
>             col = cls.__table__.c[name]
>         except KeyError:
>             log.error("Invalid column name: %s", name)
>             return None
>
>         return (getattr(cls, col.name), json_key)
>
>
> We then have a `_build_column` method that essentially is responsible for
> retrieving the details necessary to construct the column for the select
> statement necessary for constructing the SQLAlchemy ORM query:
>
> def _build_column(self):
>     field_name = self.db.get("column")
>     model = self._object.get("model")
>
>     column_type, column_key = self.db.get("type"), self.db.get("key")
>     select_column, json_key = model.get_column_and_json_key_from_sql_name(
>         field_name
>     )
>     select_column = self._construct_json_select_field(
>         column_type, select_column, json_key, column_key
>     )
>
>     return select_column
>
>
> What I'm trying to figure out is how to dynamically generate SQLAlchemy
> ORM statements based on the formula. The formulas can be any math equation
> using +, -, /, *, and parentheses:
>
> formula1 = '"metric:1" + "metric:2" + "metric:3"'
> formula2 = '"metric:1" + "metric:2" - "metric:3"'
> formula3 = '"metric:1" + ("metric:2" * "metric:3")'
> formula4 = '"metric:1" / "metric:2"'
> formula5 = '"metric:1 / ("metric:2" * "metric:3")'
>
> The InstrumentedAttribute objects I mentioned earlier are the select
> fields for each individual metric - what I need to figure out how to do is
> to be able to build a SQLAlchemy ORM query by parsing the formula string,
> and evaluating each operation in the context of the query. `func.sum` would
> work for formula1, but I'd need to iteratively build the formula with
> SQLAlchemy ORM helper methods for formula2 - formula5.
>
> Per +Jonathan Vanasco's last comment, I've already figured out Phase 1.
> I'm stuck on Phase 2.
>
> On Thursday, March 28, 2019 at 4:43:56 PM UTC-4, Ian Miller wrote:
>>
>> Hello all,
>>
>> I am in the process of trying to create a dynamic expression query engine
>> in an application I'm working on.
>>
>> So there is a formula that gets defined like so:
>>
>> formula = '"metric:123" + "metric:456" + "metric:789"'
>>
>> Each metric maps to a column in the database tables - long story short,
>> I'm able to retrieve the metric by ID, and instantiate an
>> InstrumentedAttribute object that has the SQLAlchemy metadata for the
>> associated column. What I'm trying to achieve is to be able to iterate
>> through the formula, and dynamically build a SQLALchemy query that maps to
>> the formula.
>>
>> For example, the formula defined above would look something like this in
>> SQL:
>>
>> SELECT post.id + campaign.id + asset.id
>> FROM post, campaign, asset
>> WHERE ......;
>>
>> The idea is to translate the above to something like:
>>
>> session.query(<sqlalchemy.orm.attributes.InstrumentedAttribute object at
>> 0x7ff9269f92b0> + <sqlalchemy.orm.attributes.InstrumentedAttribute object
>> at 0x7ff9269c5990> + <sqlalchemy.orm.attributes.InstrumentedAttribute
>> object at 0x7ff926896048>).all()
>>
>> I've tried a couple of approaches of dynamically generating the
>> SQLAlchemy ORM query, but I haven't been able to find anything that works.
>> Would anyone have any idea or tips on how to accomplish this?
>>
>> Thank you!
>>
>> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at https://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to