I have designed and written several systems like that. Some have been for use by the end-user (for those that don't understand SQL or data relationship) and some have been for the internal use of an application (to avoid hard coding queries). My experience is that it never worked like it was supposed to. It might be that my designs was flawed but it's not like I haven't tried everything I could think of.
It is a much more complex problem than what your description suggests. I actually know of a company that was run into the ground trying to solve it (well, there were other factors but this was the one that broke their back).
The last system I implemented consisted of four major components:
A data dictionary. Contains the meta data: Tables, columns, relationships, etc.
A graph. Basically a Weighted Undirected Graph using Adjacency List that represents all tables and their relationships.
A resolver. Given a start and ending node, finds the cheapest path through the graph using Dijkstra's algorithm.
A query builder. Uses the input criteria (where), the output list (select) and the path through the graph (joins) to generates SQL.
This works great for simple queries, small datasets or simple databases but once you throw a real world, large and complex database at it falls apart for several reasons. Here's just a few of them:
It is not enough to find the shortest/cheapest path through the graph.
Given multiple possible paths, the solution should optimally pass through as many of the "where"-tables as possible.
It's not obvious what nodes to chose for the start and end node and the choice will affect the result.
The criteria often needs to be qualified/scoped or the result will not be what the user expected.
For example if the user asks the car database for "car.name" where "country.name=China" expecting a list of Chinese cars they will not understand that they receive a list of European cars assembled in China because the resolver took an unexpected path through the graph.
There are many other cases where a choice, that affect the result, has to be made but the user hasn't supplied the information required to do so.
In many situation the desired type of join to use cannot be inferred form the cardinality of a relation.
The users often don't understand cardinality and most often don't even know where they will need to resolve a ambiguity with additional criteria.
The query builder don't know how it should resolve a many-to-many relation or if it should resolve it at all.
My recommendation is that you use a traditional query builder instead and concentrate your efforts on the documentation you mentioned.