PrevUpHomeNext

... or these

We have seen how to construct our query in a single C++ expression that covered nine lines. Now let's try it in smaller steps.

First we define a couple of C++ functions to build the mathematical parts of the query:

// Build a server-side expression that squares a float:
//
exprn_mapper<float>
square(const exprn_mapper<float> &arg) {
    return arg * arg;
}

// Build a server-side expression that computes the square of the
// distance between two points.
//
template<typename Point0, typename Point1>
exprn_mapper<float>
distance_squared(const Point0 &point0, const Point1 &point1) {
    return square(point0.x - point1.x) + square(point0.y - point1.y);
}

Then we build the query in stages:

const query<cinema> local_cinemas =
    cinemas
    .where(distance_squared(cinemas->location, my_place) <= square(max_radius));

const query<std::tuple<cinema, screen>> local_cinemas_and_their_screens =
    local_cinemas
    .inner_join(
        screens,
        screens->cinema_id == local_cinemas->id
    );

const query<std::tuple<std::tuple<cinema, screen>, movie>> local_cinemas_and_their_screens_and_current_movies =
    local_cinemas_and_their_screens
    .inner_join(
        movies,
        movies->id == local_cinemas_and_their_screens->get<1>().current_movie_id
    );

const query<std::string> titles_playing_nearby =
    local_cinemas_and_their_screens_and_current_movies
    .select(local_cinemas_and_their_screens_and_current_movies->get<1>().title)
    .distinct();

That spells out what was implicit in our first attempt, but now that I see it I don't entirely like it. The two joined queries (locals_cinemas_and_their_screens and local_cinemas_and_their_screens_and_current_movies) both produce tuples containing largely unused information. It's probably not a performance issue, because those queries themselves are never executed. (The only query that is executed is the final one, titles_playing_nearby, and it has enough information to let the DBMS make sensible optimizations.) Nevertheless the tuples complicate the C++ source code, and for that reason I prefer the following (with the definition of local_cinemas unchanged):

const query<screen> local_screens =
    local_cinemas
    .inner_join(screens, screens->cinema_id == local_cinemas->id)
    .select(*screens);

const query<movie> local_movies =
    local_screens
    .inner_join(movies, movies->id == local_screens->current_movie_id)
    .select(*movies);

const query<std::string> titles_playing_nearby =
    local_movies
    .select(local_movies->title)
    .distinct();

The combination of inner_join() and select(), in the definitions of local_screens and local_movies, serves to jump across from one query (or table) to another. Quince has a convenience function, jump(), whose job is to abbreviate that pattern. So my last revision is:

const query<screen> local_screens =
    local_cinemas
    .jump(screens, screens->cinema_id == local_cinemas->id);

const query<movie> local_movies =
    local_screens
    .jump(movies, movies->id == local_screens->current_movie_id);

const query<std::string> titles_playing_nearby =
    local_movies
    .select(local_movies->title)
    .distinct();

PrevUpHomeNext