September 12, 2015
On Friday, 11 September 2015 at 23:47:42 UTC, Andrei Alexandrescu wrote:
> 1. Use lambdas, which seem to already do what you want:
>
> db.get!Person.filter!(p => p.age > 21 && p.name == "Peter")
>
> The way this'd go, the db.get!Person() call returns an input range of Person. Presumably introspection is being used to bind fields in the query such as "age" and "name" to static field names in struct Person. Then good old std.algorithm.filter takes care of the rest.

I'm instantiating the lambda with a fake p to capture the expression so I can translate it to whatever SQL, MongoDB, columnar db is used.

> 2. If you want to embed real SQL into D, use string-based DSLs.

Strings don't capture context, aren't typechecked, and require a complex CTFE parser.

db.get!Person.where!"age > 21 & name = ?"(name);


September 12, 2015
On 12-Sep-2015 23:08, Martin Nowak wrote:
> On Friday, 11 September 2015 at 23:47:42 UTC, Andrei Alexandrescu wrote:
>> 1. Use lambdas, which seem to already do what you want:
>>
>> db.get!Person.filter!(p => p.age > 21 && p.name == "Peter")
>>
>> The way this'd go, the db.get!Person() call returns an input range of
>> Person. Presumably introspection is being used to bind fields in the
>> query such as "age" and "name" to static field names in struct Person.
>> Then good old std.algorithm.filter takes care of the rest.
>
> I'm instantiating the lambda with a fake p to capture the expression so
> I can translate it to whatever SQL, MongoDB, columnar db is used.
>
>> 2. If you want to embed real SQL into D, use string-based DSLs.
>
> Strings don't capture context, aren't typechecked, and require a complex
> CTFE parser.
>
> db.get!Person.where!"age > 21 & name = ?"(name);
>
>

What if we add generic string interpolation a-la:

s"$age > 21 && name = ${someobj.field}"

would translate to:
AliasSeq!("",age," > 21 && name = ", someobj.field, "");

Thoughts? I think it was proposed before by Timothy Cour.

-- 
Dmitry Olshansky
September 12, 2015
On Friday, 11 September 2015 at 23:19:54 UTC, Timon Gehr wrote:
>> Does anyone have a different idea how to make a nice query language?
>> db.get!Person.where!(p => p.age > 21 && p.name == "Peter")
>>
>
> You could give up on operator syntax.

That's what I did in the prototype, p.age.gt(21), but it's somewhat ugly.

September 12, 2015
On Fri, 11 Sep 2015 21:40:58 +0200, Martin Nowak <code+news.digitalmars@dawg.eu> wrote:
> Does anyone have a different idea how to make a nice query language?
> db.get!Person.where!(p => p.age > 21 && p.name == "Peter")

Django's approach is, IMO,  the cleverest and least magical one while keeping it expressive and efficient:

    Person.objects.filter(age__gt=21, name__eq='peter')

The input of `filter` is keyword argument style. So, filter can access the dynamic argument names, in addition to their values. This makes it easy to understand user's intent without the need to overloaded operators or black magic (like repeated reflections Hibernate uses) and the syntax stays clean from user's perspective as well.

-- 
--
Bahman Movaqar
September 13, 2015
On 09/12/2015 04:08 PM, Martin Nowak wrote:
> On Friday, 11 September 2015 at 23:47:42 UTC, Andrei Alexandrescu wrote:
>> 1. Use lambdas, which seem to already do what you want:
>>
>> db.get!Person.filter!(p => p.age > 21 && p.name == "Peter")
>>
>> The way this'd go, the db.get!Person() call returns an input range of
>> Person. Presumably introspection is being used to bind fields in the
>> query such as "age" and "name" to static field names in struct Person.
>> Then good old std.algorithm.filter takes care of the rest.
>
> I'm instantiating the lambda with a fake p to capture the expression so
> I can translate it to whatever SQL, MongoDB, columnar db is used.

Yah, understood. Problem here is the approach is bound to run into walls at every few steps. Say you fix the comparisons to work. Then you have operators such as LIKE that are necessary yet not representable in D. So more tricks are in order. This is what I've seen with ET in C++ - an endless collection of tricks to achieve modest outcomes at enormous costs.

>> 2. If you want to embed real SQL into D, use string-based DSLs.
>
> Strings don't capture context, aren't typechecked, and require a complex
> CTFE parser.
>
> db.get!Person.where!"age > 21 & name = ?"(name);

Understood as well. I figure from the example you have binding in mind, which is good. At some point you need to bite the bullet and write a parser for the relational query language you want there.


Andrei

September 13, 2015
On Sunday, 13 September 2015 at 03:03:44 UTC, Andrei Alexandrescu wrote:
> On 09/12/2015 04:08 PM, Martin Nowak wrote:
>> On Friday, 11 September 2015 at 23:47:42 UTC, Andrei Alexandrescu wrote:
>>> 1. Use lambdas, which seem to already do what you want:
>>>
>>> db.get!Person.filter!(p => p.age > 21 && p.name == "Peter")
>>>
>>> The way this'd go, the db.get!Person() call returns an input range of
>>> Person. Presumably introspection is being used to bind fields in the
>>> query such as "age" and "name" to static field names in struct Person.
>>> Then good old std.algorithm.filter takes care of the rest.
>>
>> I'm instantiating the lambda with a fake p to capture the expression so
>> I can translate it to whatever SQL, MongoDB, columnar db is used.
>
> Yah, understood. Problem here is the approach is bound to run into walls at every few steps. Say you fix the comparisons to work. Then you have operators such as LIKE that are necessary yet not representable in D. So more tricks are in order. This is what I've seen with ET in C++ - an endless collection of tricks to achieve modest outcomes at enormous costs.
>

Once gain, that says more about C++ than anything else. C# have such mechnism and users seems very happy with it.

>>> 2. If you want to embed real SQL into D, use string-based DSLs.
>>
>> Strings don't capture context, aren't typechecked, and require a complex
>> CTFE parser.
>>
>> db.get!Person.where!"age > 21 & name = ?"(name);
>
> Understood as well. I figure from the example you have binding in mind, which is good. At some point you need to bite the bullet and write a parser for the relational query language you want there.
>

That sounds like a bigger problem than whatever you mentioned before.

September 13, 2015
On Sunday, 13 September 2015 at 03:03:44 UTC, Andrei Alexandrescu wrote:
> Yah, understood. Problem here is the approach is bound to run into walls at every few steps. Say you fix the comparisons to work. Then you have operators such as LIKE that are necessary

like(Person.Name,"peter%")

Person.Name == pattern("peter%")

> yet not representable in D. So more tricks are in order. This is what I've seen with ET in C++ - an endless collection of tricks to achieve modest outcomes at enormous costs.

FWIW, Guido wrote a similar query language to what Martin proposes for Google App Engine called NDB:

https://cloud.google.com/appengine/docs/python/ndb/queries

They already had a string query language called GQL:

https://cloud.google.com/appengine/docs/python/datastore/gqlreference

I never use GQL, because the NDB solution is superior in every way.

GQL is only for the REPL.

>>> 2. If you want to embed real SQL into D, use string-based DSLs.
>>
>> Strings don't capture context, aren't typechecked, and require a complex
>> CTFE parser.
>>
>> db.get!Person.where!"age > 21 & name = ?"(name);
>
> Understood as well. I figure from the example you have binding in mind, which is good. At some point you need to bite the bullet and write a parser for the relational query language you want there.

No. D should stop abusing strings. Strings should never contain references to type names. It makes it impossible to transpile D in the general case.

Walter is unlikely to change his mind on opCmp, because he has put a lot of emphasis on that restriction since D1, but I think he overreacted on some C++ libraries being less than user friendly.

The only thing you achieve is that people will create even uglier hacks to get what they want, like using a thread local global AST root pointer and build the AST as hidden side effects at runtime instead.

September 13, 2015
Am 11.09.2015 um 21:40 schrieb Martin Nowak:
> I find the reasons for turining down my ER a bit moot.
>
> [Issue 14593 – operator overloading can't be used with expression
> templates](https://issues.dlang.org/show_bug.cgi?id=14593)
>
> (...)

The missing support for overloading the individual relational operators is something that has often turned out very limiting to me in the past, too. The opCmp approach is good as a default, but explicitly disallowing implementation of the individual operators seems arbitrary.

> Now expression templates make an awful DSL when being used to create a
> HTML formatter, but when the original semantic of the operators is
> preserved they are really powerful b/c the compiler already handles
> typechecking, operator precedence, and captures variables.
>
> Does anyone have a different idea how to make a nice query language?
> db.get!Person.where!(p => p.age > 21 && p.name == "Peter")

I had played around with some ideas for a similar project, but didn't find a really satisfying solution:
https://github.com/rejectedsoftware/dotter/blob/11ec72325e76c3329a58545526940c1df5328a2d/source/dotter/orm.d#L320
September 13, 2015
I am pretty sure I'd prefer hygienic string DSL (with explicit alias list for used context symbols, like in Diet) over LINQ-like magic.
September 13, 2015
On Saturday, 12 September 2015 at 22:38:53 UTC, Bahman Movaqar wrote:
> Django's approach is, IMO,  the cleverest and least magical one while keeping it expressive and efficient:
>
>     Person.objects.filter(age__gt=21, name__eq='peter')

As the main examples in this thread are for ORMs, I think the best case for more powerful operator overloading is the very popular Python library SQLAlchmey. SQLAlchmey is the reason that any project of mine that requires a database is going to be in Python.

Here is a real code sample from one of my projects from my day job:

shipped = session.query(
        func.sum(
            InvoiceHistoryDetail.QuantityShipped * InvoiceHistoryDetail.UnitPrice
        ).label("sum")
).join(
        InvoiceHistoryHeader,
        InvoiceHistoryHeader.InvoiceNo == InvoiceHistoryDetail.InvoiceNo
).join(
        Customer,
        and_(
            InvoiceHistoryHeader.ARDivisionNo == Customer.ARDivisionNo,
            InvoiceHistoryHeader.CustomerNo == Customer.CustomerNo
        )
).filter(
        InvoiceHistoryHeader.ShipDate >= fiscal_month.PeriodStartingDate,
        InvoiceHistoryHeader.ShipDate == datetime.date.today() - datetime.timedelta(days=1),
        InvoiceHistoryHeader.SalesOrderNo != None,
        Customer.UDF_PARTICIPATION.in_(participation)
).one()

Note how complex operator overloading is used to make very readable JOIN and WHERE clauses. One other cool thing to note is the func function, which is a generator function that allows expressing any SQL function in Python by translating the name after the dot to the function name is the final SQL. So func.whatever(model.field) would become WHATEVER(`model.field`) in the SQL.

I know that this effect is much harder to create in a explicitly and strongly typed language, but I just wanted to show a real world example of how these could be used to great effect.