zussel / matador

Take your appclication by the horns
http://zussel.github.io/matador
GNU General Public License v3.0
63 stars 22 forks source link

Implement new interface for session and object_view<T> to make it more fluent #58

Open saki7 opened 8 years ago

saki7 commented 8 years ago

Motivation

What to do

The API

// Returns object_view<Book>
auto const books = session.select<Book>().where(oos::column("published_at") >= 2015).limit(100).execute();

oos::variable<std::string> author_var(oos::make_var(&Book::author));
auto const book = books.find_if(author_var == "John Smith");

See also

The issue #56.

saki7 commented 8 years ago

@zussel said in the issue #56 that you can't mix query<T> and object_view<T>, but I'd suggest it to create a base class representing composed queries then make query<T> and object_query<T> (the new class, or whatsoever) inherit it to avoid duplicate in the implementation...

zussel commented 8 years ago

object_query<t> came to my mind as well. Good suggestion.

I can't derive form query<T> because query<T> contains beside the select interfaces all other statement interfaces (like create, drop, ...). So I'll implement the few methods right away.

The lazy loading issue might be a bigger one when it comes to relations and dereferencing them. Just have a look at the docs (has_one and has_many).

But after our discussion that feature looks really promising and tangible to me.

saki7 commented 8 years ago

I thought not to derive from query<T> (sorry for my bad English), I meant something like this:

template<class T, class Self>
class basic_query
{
public:
    typedef Self self_type;

    template<class COND>
    self_type& where(COND const& cond)
    {
        // Just an another pseudo-code here
        conds_.push_back(cond);
        return *static_cast<self_type*>(this);
    }

    self_type& limit(unsigned count)
    {
        conds_.push_back(oos::limit(count));
        return *static_cast<self_type*>(this);
    }

     // Other SQL related common methods go here...
};

template<class T>
class object_query : public basic_query<T, object_query<T>>
{
public:
    object_view<T> execute()
    {
        // Since session::select() is intended to return this class (object_query<T>),
        // the internal persistence object should be available here
        publish_sql(persistence_);
    }
};

template<class T>
class query : public basic_query<T, query<T>>
{
public:
    void create() { /* do something */ }
    void drop() { /* do something */ }

    result<T> execute(connection& c)
    {
        // You know, a `connection` is required for query<T>s.
        publish_sql(c);
    }
};

Chaining + inheritance = wizardly code I guess... I think that the downcasting of static_cast<self_type*>(this) is well-formed since we already know the target class is derived from basic_query<T>. Or I may be missing something.

This way I think you can avoid the statement function duplicates for object_query<T> and query<T>.

saki7 commented 8 years ago

And for dereferencing relations in lazy loading you can google "N+1 problem" - the library should have an eager-loading feature to avoid it.

It's quite easy though - just publish SELECT "books".* FROM "books" WHERE "books"."id" IN (45, 56, 98, ...)

The model objects in Ruby on Rails (ActiveRecord) have a method includes("related_table_name") to set which table to load. You don't need to eager load all relations in a query; just specify the ones you need and it's ok.

zussel commented 7 years ago

Oh yes, I know this pattern. It's called _Curiously recurring template pattern_. And I used it at some places in my code. That's indeed a good possibility to "derive" from this kind of interface.

Didn't know the N+1 problem. I'll take a look at it. Maybe it can solve the lazy loading problem with it. Thank you for the hint!

As for the next release there are just to issues to solve:

So things developing well :smile: