+++ /dev/null
-<html><head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Grammar of JSON Queries</title><meta name="generator" content="DocBook XSL-NS Stylesheets V1.74.3-pre"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e1"></a>Grammar of JSON Queries</h2></div></div><hr></div><p>
- <span class="author"><span class="firstname">Scott</span> <span class="surname">McKellar</span></span>
- </p><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e19"></a>Introduction</h3></div></div></div><p> The format of this grammar approximates Extended Backus-Naur notation. However it is
- intended as input to human beings, not to parser generators such as Lex or Yacc. Do not
- expect formal rigor. Sometimes narrative text will explain things that are clumsy to
- express in formal notation. More often, the text will restate or summarize the formal
- productions. </p><p> Conventions: </p><div class="orderedlist"><ol type="1"><li><p>The grammar is a series of productions.</p></li><li><p>A production consists of a name, followed by "::=", followed by a definition
- for the name. The name identifies a grammatical construct that can appear on the
- right side of another production.</p></li><li><p>Literals (including punctuation) are enclosed in 'single quotes', or in
- "double quotes" if case is not significant.</p></li><li><p>A single quotation mark within a literal is escaped with a preceding
- backslash: 'dog\'s tail'.</p></li><li><p>If a construct can be defined more than one way, then the alternatives may
- appear in separate productions; or, they may appear in the same production,
- separated by pipe symbols. The choice between these representations is of only
- cosmetic significance.</p></li><li><p>A construct enclosed within square brackets is optional.</p></li><li><p>A construct enclosed within curly braces may be repeated zero or more
- times.</p></li><li><p>JSON allows arbitrary white space between tokens. To avoid ugly clutter, this
- grammar ignores the optional white space. </p></li><li><p>In many cases a production defines a JSON object, i.e. a list of name-value
- pairs, separated by commas. Since the order of these name/value pairs is not
- significant, the grammar will not try to show all the possible sequences. In
- general it will present the required pairs first, if any, followed by any
- optional elements.</p></li></ol></div><p> Since both EBNF and JSON use curly braces and square brackets, pay close attention to
- whether these characters are in single quotes. If they're in single quotes, they are
- literal elements of the JSON notation. Otherwise they are elements of the EBNF notation.
- </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e56"></a>Primitives</h3></div></div></div><p> We'll start by defining some primitives, to get them out of the way. They're mostly
- just what you would expect. </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[1]</td><td align="right" valign="top" width="10%"><a name="ebnf.string"></a> string </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '"' chars '"' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[2]</td><td align="right" valign="top" width="10%"><a name="ebnf.chars"></a> chars </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> any valid sequence of UTF-8 characters, with certain special characters
- escaped according to JSON rules </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[3]</td><td align="right" valign="top" width="10%"><a name="ebnf.integer_literal"></a> integer_literal </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> [ sign ] digit { digit } </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[4]</td><td align="right" valign="top" width="10%"><a name="ebnf.sign"></a> sign </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '+' | '-' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[5]</td><td align="right" valign="top" width="10%"><a name="ebnf.digit"></a> digit </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%">digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'</td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[6]</td><td align="right" valign="top" width="10%"><a name="ebnf.integer_string"></a> integer_string </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '"' integer_literal '"' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[7]</td><td align="right" valign="top" width="10%"><a name="ebnf.integer"></a> integer </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> integer_literal | integer_string </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[8]</td><td align="right" valign="top" width="10%"><a name="ebnf.number"></a> number </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> any valid character sequence that is numeric according to JSON rules </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> When json_query requires an integral value, it will usually accept a quoted string
- and convert it to an integer by brute force – to zero if necessary. Likewise it may
- truncate a floating point number to an integral value. Scientific notation will be
- accepted but may not give the intended results. </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[9]</td><td align="right" valign="top" width="10%"><a name="ebnf.boolean"></a> boolean </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> 'true' | 'false' | string | number </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The preferred way to encode a boolean is with the JSON reserved word true or false,
- in lower case without quotation marks. The string <code class="literal">true</code>, in upper,
- lower, or mixed case, is another way to encode true. Any other string evaluates to
- false. </p><p> As an accommodation to perl, numbers may be used as booleans. A numeric value of 1
- means true, and any other numeric value means false. </p><p> Any other valid JSON value, such as an array, will be accepted as a boolean but
- interpreted as false. </p><p> The last couple of primitives aren't really very primitive, but we introduce them
- here for convenience: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[10]</td><td align="right" valign="top" width="10%"><a name="ebnf.class_name"></a> class_name </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> string </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> A class_name is a special case of a string: the name of a class as defined by the
- IDL. The class may refer either to a database table or to a source_definition, which is
- a subquery. </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[11]</td><td align="right" valign="top" width="10%"><a name="ebnf.field_name"></a> field_name </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> string </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> A field_name is another special case of a string: the name of a non-virtual field as
- defined by the IDL. A field_name is also a column name for the table corresponding to
- the relevant class. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e185"></a>Query</h3></div></div></div><p> The following production applies not only to the main query but also to most
- subqueries. </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[12]</td><td align="right" valign="top" width="10%"><a name="ebnf.query"></a> query </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{'<br> '"from"' ':' from_list<br> [ ',' '"select"' ':' select_list
- ]<br> [ ',' '"where"' ':' where_condition ]<br> [ ',' '"having"' ':'
- where_condition ]<br> [ ',' '"order_by"' ':' order_by_list ]<br> [ ','
- '"limit"' ':' integer ]<br> [ ',' '"offset"' ':' integer ]<br> [ ','
- '"distinct"' ':' boolean ]<br> [ ',' '"no_i18n"' ':' boolean ]<br> '}'
- </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> Except for the <code class="literal">"distinct"</code> and <code class="literal">no_i18n</code> entries,
- each name/value pair represents a major clause of the SELECT statement. The name/value
- pairs may appear in any order. </p><p> There is no name/value pair for the GROUP BY clause, because json_query generates it
- automatically according to information encoded elsewhere. </p><p> The <code class="literal">"distinct"</code> entry, if present and true, tells json_query that
- it may have to create a GROUP BY clause. If not present, it defaults to false. </p><p> The <code class="literal">"no_i18n"</code> entry, if present and true, tells json_query to
- suppress internationalization. If not present, it defaults to false. (Note that
- <code class="literal">"no_i18n"</code> contains the digit one, not the letter ell.) </p><p> The values for <code class="literal">limit</code> and <code class="literal">offset</code> provide the
- arguments of the LIMIT and OFFSET clauses, respectively, of the SQL statement. Each
- value should be non-negative, if present, or else the SQL won't work. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e252"></a>FROM Clause</h3></div></div></div><p>
- The object identified by <code class="literal">“from”</code> encodes the FROM clause of
- the SQL. The associated value may be a string, an array, or a JSON object.
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[13]</td><td align="right" valign="top" width="10%"><a name="ebnf.from_list_0"></a> from_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> class_name </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- If <code class="literal">from_list</code> is a <code class="literal">class_name</code>, the
- json_query inserts the corresponding table name or subquery into the FROM
- clause, using the <code class="literal">class_name</code> as an alias for the table
- or subquery. The class must be defined as non-virtual in the IDL.
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[14]</td><td align="right" valign="top" width="10%"><a name="ebnf.from_list_1"></a> from_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '[' string { ',' parameter } ']' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[15]</td><td align="right" valign="top" width="10%"><a name="ebnf.parameter"></a> parameter </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> string | number | 'null' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- If from_list is a JSON array, then it represents a table-like function from
- which the SQL statement will select rows, using a SELECT clause consisting
- of “SELECT *” (regardless of the select_list supplied by the method parameter).
- </p><p>
- The first entry in the array is the name of the function. It must be a string
- naming a stored function. Each subsequent entry is a function parameter. If
- it is a string or a number, json_query will insert it into a comma-separated
- parameter list, enclosed in quotes, with any special characters escaped as needed.
- If it is the JSON reserved word <code class="literal">null</code>, json_query will insert
- it into the parameter list as a null value.
- </p><p>
- If <code class="literal">from_list</code> is a JSON object, it must contain exactly one entry.
- The key of this entry must be the name of a non-virtual class defined in the IDL.
- This class will be the top-level class of the FROM clause, the only one named
- outside of a JOIN clause.
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[16]</td><td align="right" valign="top" width="10%"><a name="ebnf.from_list_2"></a> from_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' class_name ':' join_list '}' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[17]</td><td align="right" valign="top" width="10%"><a name="ebnf.join_list_0"></a> join_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> class_name </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[18]</td><td align="right" valign="top" width="10%"><a name="ebnf.join_list_1"></a> join_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' join_def { ',' join_def } '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- If the associated data is a <code class="literal">class_name</code>, json_query will
- construct an INNER JOIN clause joining the class to the top-level clause,
- using the columns specified by the IDL for such a join.
- </p><p>
- Otherwise, the associated data must be a JSON object with one or more entries,
- each entry defining a join:
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[19]</td><td align="right" valign="top" width="10%"><a name="ebnf.join_def"></a> join_def </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%">
- class_name ':'<br>
- '{'<br>
- [ '”type”' ':' string ]<br>
- [ '”field”' ':' field_name ]<br>
- [ '”fkey”' ':' field_name ]<br>
- [ '”filter”' ':' where_condition ]<br>
- [ '”filter_op”' ':' string ]<br>
- [ '”join”' ':' join_list ]<br>
- '}'
- </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- The data portion of the <code class="literal">“join_type”</code> entry tells json_query
- whether to use a left join, right join, full join, or inner join. The values
- <code class="literal">“left”</code>, <code class="literal">“right”</code>, and <code class="literal">“full”</code>,
- in upper, lower, or mixed case, have the obvious meanings. If the
- <code class="literal">“join_type”</code> entry has any other value, or is not present,
- json_query constructs an inner join.
- </p><p>
- The <code class="literal">“field”</code> and <code class="literal">“fkey”</code> attributes specify the
- columns to be equated in the join condition. The <code class="literal">“field”</code>
- attribute refers to the column in the joined table, i.e. the one named by the
- <code class="literal">join_def</code>. The <code class="literal">“fkey”</code> attribute refers to the
- corresponding column in the other table, i.e. the one named outside the
- <code class="literal">join_def</code> – either the top-level table or a table named by some
- other <code class="literal">join_def</code>.
- </p><p>
- It may be tempting to suppose that <code class="literal">“fkey”</code> stands for “foreign key”,
- and therefore refers to a column in the child table that points to the key of a
- parent table. Resist the temptation; the labels are arbitrary. The json_query
- method doesn't care which table is the parent and which is the child.
- </p><p>
- These relationships are best explained with an example. The following
- <code class="literal">from_list</code>:
- </p><div class="informalexample"><pre class="programlisting">
- {
- "aou": {
- "asv": {
- "type" : "left",
- "fkey" : "id",
- "field" : "owner"
- }
- }
- }
- </pre></div><p>
- ...turns into the following FROM clause:
- </p><div class="informalexample"><pre class="programlisting">
- FROM
- actor.org_unit AS "aou"
- LEFT JOIN action.survey AS "asv"
- ON ( "asv".owner = "aou".id )
- </pre></div><p>
- Note in this example that <code class="literal">“fkey”</code> refers to a column of the
- class <code class="literal">“aou”</code>, and <code class="literal">“field”</code> refers to a
- column of the class <code class="literal">“asv”</code>.
- </p><p>
- If you specify only one of the two columns, json_query will try to identify the
- other one from the IDL. However, if you specify only the column from the parent
- table, this attempt will probably fail.
- </p><p>
- If you specify both columns, json_query will use the column names you specify,
- without verifying them with a lookup in the IDL. By this means you can perform
- a join using a linkage that the IDL doesn't define. Of course, if the columns
- don't exist in the database, the query will fail when json_query tries to execute it.
- </p><p>
- Using the columns specified, either explicitly or implicitly, the json_query
- method constructs a join condition. With raw SQL it is possible (though
- rarely useful) to join two tables by an inequality. However the json_query
- method always uses a simple equality condition.
- </p><p>
- Using a <code class="literal">“filter”</code> entry in the join_def, you can apply one
- or more additional conditions to the JOIN clause, typically to restrict the
- join to certain rows of the joined table. The data associated with the
- <code class="literal">“filter”</code> key is the same sort of
- <code class="literal">where_condition</code> that you use for a WHERE clause
- (discussed below).
- </p><p>
- If the string associated with the <code class="literal">“filter_op”</code> entry is
- <code class="literal">“OR”</code> in upper, lower, or mixed case, then the json_query
- method uses OR to connect the standard join condition to any additional
- conditions supplied by a <code class="literal">“filter”</code> entry.
- </p><p>
- (Note that if the <code class="literal">where_condition</code> supplies multiple
- conditions, they will be connected by AND. You will probably want to move
- them down a layer – enclose them in parentheses, in effect – to avoid a
- confusing mixture of ANDs and ORs.)
- </p><p>
- If the <code class="literal">“filter_op”</code> entry carries any other value, or if
- it is absent, then the json_query method uses AND. In the absence of a
- <code class="literal">“filter”</code> entry, <code class="literal">“filter_op”</code> has no effect.
- </p><p>
- A <code class="literal">“join”</code> entry in a <code class="literal">join_def</code> specifies
- another layer of join. The class named in the subjoin is joined to the class
- named by the <code class="literal">join_def</code> to which it is subordinate. By this
- means you can encode multiple joins in a hierarchy.
- </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e504"></a>SELECT Clause</h3></div></div></div><p>
- If a query does not contain an entry for <code class="literal">“select”</code>, json_query
- will construct a default SELECT clause. The default includes every non-virtual
- field from the top-level class of the FROM clause, as defined by the IDL. The
- result is similar to SELECT *, except:
- </p><div class="itemizedlist"><ul type="disc"><li><p>The default includes only the fields defined in the IDL.</p></li><li><p>The columns will appear in the same order in which they appear in the IDL,
- regardless of the order in which the database defines them.</p></li></ul></div><p>
- There are other ways to specify a default SELECT list, as shown below.
- </p><p>
- If a <code class="literal">"select"</code> entry is present, the associated value must
- be a JSON object, keyed on class names:
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[20]</td><td align="right" valign="top" width="10%"><a name="ebnf.select_list"></a> select_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' class_name ':' field_list { ',' class_name ':' field_list } '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- The <code class="literal">class_name</code> must identify either the top-level class or
- a class belonging to one of the joins. Otherwise json_query will silently
- ignore the <code class="literal">select_list</code>.
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[21]</td><td align="right" valign="top" width="10%"><a name="ebnf.field_list_0"></a> field_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> 'null' | '”*”' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- If a field_list is either the JSON reserved word <code class="literal">null</code>
- (in lower case) or an asterisk in double quotes, json_query constructs a
- default SELECT list – provided that the class is the top-level class of the
- query. If the class belongs to a join somewhere, json_query ignores the
- <code class="literal">field_list</code>.
- </p><p>
- More commonly, the <code class="literal">field_list</code> is a JSON array of zero or
- more field specifications:
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[22]</td><td align="right" valign="top" width="10%"><a name="ebnf.field_list_1"></a> field_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '[' [ field_spec { ',' field_spec } ] ']' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- If the array is empty, json_query will construct a default SELECT list for
- the class – again, provided that the class is the top-level class in the query.
- </p><p>
- In the simplest case, a field specification may name a non-virtual field
- defined in the IDL:
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[23]</td><td align="right" valign="top" width="10%"><a name="ebnf.field_spec_0"></a> field_spec </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> field_name </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- In some cases json_query constructs a call to the
- <code class="literal">oils_i18n_xlate</code> function to internationalize the value of the
- selected column. Specifically, it does so if all the following are true:
- </p><div class="itemizedlist"><ul type="disc"><li><p>the settings file defines a locale;</p></li><li><p>in the field definition for the field in the IDL, the tag
- <code class="literal">“il8n”</code> is present and true;</p></li><li><p>the query does <span class="emphasis"><em>not</em></span> include the
- <code class="literal">"no_il8n"</code> tag (or includes it with a value of false).</p></li></ul></div><p>
- A field specification may be a JSON object:
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[24]</td><td align="right" valign="top" width="10%"><a name="ebnf.field_spec_1"></a> field_spec </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%">
- '{'<br>
- '”column”' ':' <br>
- [ ',' '”alias”' ':' string ]<br>
- [ ',' '”aggregate”' ':' boolean ]<br>
- [ ',' transform_spec ]<br>
- '}'
- </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- The <code class="literal">“column”</code> entry provides the column name, which must
- be defined as non-virtual in the IDL.
- </p><p>
- The <code class="literal">“alias”</code> entry provides a column alias. If no alias
- is specified, json_query uses the column name as its own alias.
- </p><p>
- The <code class="literal">“aggregate”</code> entry has no effect on the SELECT clause
- itself. Rather, it affects the construction of a GROUP BY class. If there
- is an <code class="literal">“aggregate”</code> entry for any field, then json_query builds
- a GROUP BY clause listing every column that is <span class="emphasis"><em>not</em></span> tagged
- for aggregation (or that carries an <code class="literal">“aggregate”</code> entry with
- a value of false). If <span class="emphasis"><em>all</em></span> columns are tagged for
- aggregation, then json_query omits the GROUP BY clause.
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[25]</td><td align="right" valign="top" width="10%"><a name="ebnf.transform_spec_0"></a> transform_spec </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%">
- '”transform”' ':' string ]<br>
- [ ',' '”result_field” ':' string ]<br>
- [ ',' '”params” ':' param_list ]
- </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- When a <code class="literal">transform_spec</code> is present, json_query selects the
- return value of a function instead of selecting the column directly. The entry
- for <code class="literal">“transform”</code> provides the name of the function, and the
- column name (as specified by the <code class="literal">“column”</code> tag), qualified by
- the class name, is the argument to the function. For example, you might use such
- a function to format a date or time, or otherwise transform a column value.
- You might also use an aggregate function such as SUM, COUNT, or MAX (possibly
- together with the <code class="literal">“aggregate”</code> tag).
- </p><p>
- The <code class="literal">“result_field”</code> entry, when present, specifies a subcolumn
- of the function's return value. The resulting SQL encloses the function call
- in parentheses, and follows it with a period and the subcolumn name.
- </p><p>
- The <code class="literal">“params”</code> entry, if present, provides a possibly empty
- array of additional parameter values, either strings, numbers, or nulls:
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[26]</td><td align="right" valign="top" width="10%"><a name="ebnf.param_list"></a> param_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '[' [ parameter { ',' parameter } ] ']' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p>
- Such parameter values are enclosed in single quotes, with any special characters
- escaped as needed, and inserted after the column name as additional parameters
- to the function. You might, for example, use an additional parameter to provide
- a format string for a reformatting function.
- </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e721"></a>WHERE Clause</h3></div></div></div><p> There are two types of <code class="literal">where_condition</code>: objects and arrays.
- Of these, the object type is the more fundamental, and occurs at some level in every
- <code class="literal">where_condition</code>. The array type is mainly a way of circumventing
- a limitation of the object type. </p><p> The object type of <code class="literal">where_condition</code> is a comma-separated list
- of one or more <code class="literal">conditions</code>: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[27]</td><td align="right" valign="top" width="10%"><a name="ebnf.where_condition_0"></a> where_condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' condition { ',' condition } '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The generated SQL will include a code fragment for each <code class="literal">condition</code>,
- joined by AND (or in some cases by OR, as described below). </p><p> As usual for entries in a JSON object, each <code class="literal">condition</code> consists
- of a unique string to serve as a key, a colon, and an associated value. </p><p> The key string may be the name of a column belonging to the relevant table, or
- it may be an operator string. In order to distinguish it from any possible column
- name, an operator string always begins with a plus sign or minus sign. </p><p> JSON requires that every key string be unique within an object. This requirement
- imposes some awkward limitations on a JSON query. For example, you might want to
- express two conditions for the same column: id > 10 and id != 25. Since each of
- those conditions would have the same key string, namely “id”, you can't put them
- into the same JSON object. </p><p> The solution is to put such conflicting conditions in separate JSON objects, and
- put the objects into an array: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[28]</td><td align="right" valign="top" width="10%"><a name="ebnf.where_condition_1"></a> where_condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '[' where_condition { ',' where_condition } ']' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The resulting SQL encloses each subordinate set of <code class="literal">conditions</code>
- in parentheses, and connects the sets with AND (or in some cases OR, as described
- below). It's possible to put only a single <code class="literal">where_condition</code> in
- the array; the result is to add a layer of parentheses around the condition. </p><p> There are two kinds of <code class="literal">condition</code> where the operator begins
- with a plus sign. In the simpler case, the associated data is simply a column name:
- </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[29]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_0"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> plus_class ':' field_name </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> A <code class="literal">plus_class</code> is a string that begins with a plus sign.
- The rest of the string, after the plus sign, must be the class name for the table
- to which the column belongs. </p><p> If the column is a boolean, then the resulting SQL uses it (as qualified by the
- class name) as a stand-alone condition. </p><p> Otherwise, this kind of syntax provides a way to place a column on the right side
- of a comparison operator. For example: </p><div class="informalexample"><pre class="programlisting">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "id": {
- ">": { "+aou":"parent_ou" }
- }
- }
- }
- </pre></div><p> The resulting SQL: </p><div class="informalexample"><pre class="programlisting">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- (
- "aou".id > ( "aou".parent_ou )
- );
- </pre></div><p> The other type of <code class="literal">condition</code> that uses a
- <code class="literal">plus_class</code> applies a specified class name to a
- <code class="literal">where_condition</code>: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[30]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_1"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> plus_class ':' where_condition </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The resulting SQL is enclosed in parentheses, and qualifies the columns with
- the specified class name. This syntax provides a mechanism to shift the class
- context – i.e. to refer to one class in a context that would otherwise refer to
- a different class. </p><p> Ordinarily the class name must be a valid non-virtual class defined in the IDL,
- and applicable to the associated <code class="literal">where_condition</code>. There is at
- least one peculiar exception. The JSON fragment: </p><div class="informalexample"><pre class="programlisting">
- "+abc": { "+xyz":"frobozz" }
- </pre></div><p> ...is rendered as: </p><div class="informalexample"><pre class="programlisting">
- ( "xyz".frobozz )
- </pre></div><p> ...even though neither <code class="literal">“abc”</code>, nor <code class="literal">“xyz”</code>,
- nor <code class="literal">“frobozz”</code> is defined in the IDL. The class name
- <code class="literal">“abc”</code> isn't used at all because the <code class="literal">“+xyz”</code>
- operator overrides it. Such a query won't fail until json_query tries
- to execute it in the database. </p><p> The other operators that may occur at this level all begin with a minus sign,
- and they all represent familiar SQL operators. For example, the
- <code class="literal">“-or”</code> operator joins the conditions within a
- <code class="literal">where_condition</code> by OR (instead of the default AND), and
- encloses them all in parentheses: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[31]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_2"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '”-or”' ':' where_condition </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> In fact the <code class="literal">“-or”</code> operator is the only way to get OR into
- the WHERE clause. </p><p> The <code class="literal">“-and”</code> operator is similar, except that it uses AND: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[32]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_3"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '”-and”' ':' where_condition </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> Arguably the <code class="literal">“-and”</code> operator is redundant, because you can
- get the same effect by wrapping the subordinate <code class="literal">where_condition</code>
- in a JSON array. Either technique merely adds a layer of parentheses, since AND
- connects successive conditions by default. </p><p> The <code class="literal">“-not”</code> operator expands the subordinate
- <code class="literal">where_condition</code> within parentheses, and prefaces the result
- with NOT: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[33]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_4"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '”-not”' ':' where_condition </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The <code class="literal">“-exists”</code> or <code class="literal">“-not-exists”</code> operator
- constructs a subquery within an EXISTS or NOT EXISTS clause, respectively: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[34]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_5"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '”-exists”' ':' query </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[35]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_6"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '”-not-exists”' ':' query </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The remaining kinds of <code class="literal">condition</code> all have a
- <code class="literal">field_name</code> on the left and some kind of <code class="literal">predicate</code>
- on the right. A <code class="literal">predicate</code> places a constraint on the value of
- the column – or, in some cases, on the value of the column as transformed by some
- function call: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[36]</td><td align="right" valign="top" width="10%"><a name="ebnf.condition_7"></a> condition </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> field_name ':' predicate </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The simplest such constraint is to require that the column have a specific value,
- or be null: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[37]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_0"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> lit_value | 'null' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[38]</td><td align="right" valign="top" width="10%"><a name="ebnf.lit_value"></a> lit_value </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> string | number </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> You can also compare a column to a literal value using some kind of inequality.
- However it's a bit more complicated because you have to specify what kind of comparison
- to make: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[39]</td><td align="right" valign="top" width="10%"><a name="ebnf.compare_op_0"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' compare_op ':' lit_value '}' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[40]</td><td align="right" valign="top" width="10%"><a name="ebnf.compare_op_1"></a> compare_op </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> string </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> A <code class="literal">compare_op</code> is a string that defines a comparison operator.
- Valid values include the following: </p><pre class="programlisting">
- = <> !=
- < > <= >=
- ~ ~* !~ !~*
- like ilike
- similar to
- </pre><p> Strictly speaking, json_query accepts any <code class="literal">compare_op</code>
- that doesn't contain semicolons or white space (or
- <code class="literal">“similar to”</code> as a special exception). As a result, it
- is possible – and potentially useful – to use a custom operator like
- <code class="literal">“>100*”</code> in order to insert an expression that would
- otherwise be difficult or impossible to create through a JSON query. The ban
- on semicolons and white space prevents certain kinds of SQL injection. </p><p> Note that json_query does <span class="emphasis"><em>not</em></span> accept two operators that
- PostgreSQL <span class="emphasis"><em>does</em></span> accept: <code class="literal">“is distinct from”</code>
- and <code class="literal">“is not distinct from”</code>. </p><p> You can also compare a column to a null value: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[41]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_1"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' compare_op ':' 'null' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The equality operator <code class="literal">“=”</code> turns into IS NULL. Any other
- operator turns into IS NOT NULL. </p><p> When a <code class="literal">compare_op</code> is paired with an array, it defines a
- function call: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[42]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_2"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' compare_op ':' '[' string { ',' parameter } ']' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The first entry in the array is the function's name. Subsequent entries in
- the array, if any, represent the parameters of the function call. They may be
- strings, numbers, or nulls. In the generated SQL, the function call appears on
- the right of the comparison. </p><p> The <code class="literal">“between”</code> operator creates a BETWEEN clause: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[43]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_3"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' “between” ':' '[' lit_value ',' lit_value ']' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> Although PostgreSQL allows a null value in a BETWEEN clause, json_query
- requires literal non-null values. It isn't sensible to use null values in a
- BETWEEN clause. A few experiments show that the results of the comparison are
- peculiar and erratic. </p><p> There are two ways to create an IN list of allowed values. The simplest is
- to put literal values into a JSON array: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[44]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_4"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '[' lit_value { ',' lit_value } ']' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> As with BETWEEN clauses, json_query does not accept null values in an IN list,
- even though PostgreSQL does allow them. Nulls are not sensible in this context
- because they never match anything. </p><p> </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1140"></a>Having Clause</h3></div></div></div><p>For the HAVING clause, json_query accepts exactly the same syntax as it accepts for
- the WHERE clause.</p><p> The other way to create an IN list is to use an explicit
- <code class="literal">“in”</code> operator with an array of literal values. This format
- also works for the <code class="literal">“not in”</code> operator: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[45]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_5"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' in_operator ';' '[' lit_value [ ',' lit_value ] ']' '}' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[46]</td><td align="right" valign="top" width="10%"><a name="ebnf.in_operator"></a> in_operator </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> “in” | “not in” </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> Another kind of IN or NOT IN clause uses a subquery instead of a list of
- values: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[47]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_6"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' in_operator ':' query '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The remaining types of <code class="literal">predicate</code> can put a function call on
- the left of the comparison, by using a <code class="literal">transform_spec</code> together
- with a <code class="literal">“value”</code> tag. The <code class="literal">transform_spec</code> is
- optional, and if you don't need it, the same SQL would in many cases be easier to
- express by other means. </p><p> The <code class="literal">transform_spec</code> construct was described earlier in
- connection with the SELECT clause, but here it is again: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[48]</td><td align="right" valign="top" width="10%"><a name="ebnf.transform_spec_1"></a> transform_spec </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%">
- '”transform”' ':' string ]<br>
- [ ',' '”result_field” ':' string ]<br>
- [ ',' '”params” ':' param_list ]
- </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> As in the SELECT clause, the <code class="literal">“transform”</code> string names the
- function. The first parameter is always the column identified by the field_name.
- Additional parameters, if any, appear in the <code class="literal">param_list</code>. The
- <code class="literal">“result_field”</code> string, if present, identifies one column of a
- multicolumn return value. </p><p> Here's a second way to compare a value to a literal value (but not to a null
- value): </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[49]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_7"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' compare_op ':' '{' '”value”' ':' lit_value<br>
- [ transform_spec ] '}' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> ...and a way to compare a value to a boolean expression: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[50]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_8"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' compare_op ':' '{' '”value”' ':' '{'<br>
- condition { ',' condition } [ transform_spec ] '}' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The final predicate is another way to put a function call on the right side
- of the comparison: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[51]</td><td align="right" valign="top" width="10%"><a name="ebnf.predicate_9"></a> predicate </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' compare_op ':' '{' '”value”' ':' '['<br>
- string { ',' parameter } ']' [ transform_spec ] '}' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> This format is available for the sake of consistency, but offers no advantage
- over the simpler version. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1278"></a>ORDER BY Clause</h3></div></div></div><p> There are two ways to encode an ORDER BY clause: as an array, or as a list.
- Either may be empty, in which case the generated SQL will not include an ORDER BY
- clause: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[52]</td><td align="right" valign="top" width="10%"><a name="ebnf.order_by_list_0"></a> order_by_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '[' ']' | '{' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> If not empty, the array contains one or more objects, each defining a sort
- field: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[53]</td><td align="right" valign="top" width="10%"><a name="ebnf.order_by_list_1"></a> order_by_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' sort_field_def { ',' sort_field_def } '}' </td><td align="left" valign="top" width="30%"> </td></tr><tr><td align="left" valign="top" width="3%">[54]</td><td align="right" valign="top" width="10%"><a name="ebnf.sort_field_def"></a> sort_field_def </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{'<br>
- '”class”' ':' class_name<br>
- ',' '”field”' ':' field_name<br>
- [ ',' '”direction”' ':' lit_value ]<br>
- [ ',' transform_spec ]<br>
- '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> The <code class="literal">“class”</code> and <code class="literal">“field”</code> entries are
- required, and of course the field must belong to the class. Furthermore, at
- least one field from the class must appear in the SELECT clause. </p><p> The <code class="literal">“direction”</code> entry, if present, specifies whether the
- sort will be ascending or descending for the column in question. If the associated
- value begins with “D” or “d”, the sort will be descending; otherwise the sort will
- be ascending. If the value is a number, it will be treated as a string that does not
- start with “D” or “d”, resulting in an ascending sort. </p><p> In the absence of a <code class="literal">“direction”</code> entry, the sort will be
- ascending. </p><p> The <code class="literal">transform_spec</code> works here the same way it works in the
- SELECT clause and the WHERE clause, enabling you to pass the column through a
- transforming function before the sort: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[55]</td><td align="right" valign="top" width="10%"><a name="ebnf.transform_spec_2"></a> transform_spec </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%">
- '”transform”' ':' string ]<br>
- [ ',' '”result_field” ':' string ]<br>
- [ ',' '”params” ':' param_list ]
- </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> When the <code class="literal">order_by_list</code> is an object instead of an array,
- the syntax is less verbose, but also less flexible. The keys for the object are
- class names: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[56]</td><td align="right" valign="top" width="10%"><a name="ebnf.order_by_list_2"></a> order_by_list </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' class_name ':' sort_class_def<br>
- { ',' class_name ':' sort_class_def } '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> Each class must be referenced in the SELECT clause. </p><p> As in the SELECT clause, all the fields for a given class must be grouped
- together. You can't sort by a column from one table, then a column from a second
- table, then another column from the first table. If you need this kind of sort,
- you must encode the ORDER BY clause as an array instead of an object. </p><p> The data associated with a <code class="literal">class_name</code> may be either an array
- or an object. If an array, it's simply a list of field names, and each field must
- belong to the class: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[57]</td><td align="right" valign="top" width="10%"><a name="ebnf.sort_class_def_0"></a> sort_class_def </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '[' field_name { ',' field_name } ']' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> With this syntax, the direction of sorting will always be ascending. </p><p> If the data is an object, the keys are field names, and as usual the fields
- must belong to the class: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[58]</td><td align="right" valign="top" width="10%"><a name="ebnf.sort_class_def_1"></a> sort_class_def </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' field_name ':' sort_class_subdef<br>
- { ',' field_name ':' sort_class_subdef } '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> Since the <code class="literal">field_name</code> is the key for the object, it cannot
- appear more than once. As a result, some kinds of sorts are not possible with this
- syntax. For example, one might want to sort by UPPER( family_name ), and then by
- family_name with case unchanged, to make sure that “diBona” comes before “Dibona”.
- For situations like this, you must encode the ORDER BY clause as an array rather
- than an object. </p><p> The data associated with each <code class="literal">field_name</code> may take either of
- two forms. In the simplest case, it's a literal value to specify the direction
- of sorting: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[59]</td><td align="right" valign="top" width="10%"><a name="ebnf.sort_class_subdef_0"></a> sort_class_subdef </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> lit_value </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> If the literal is a string starting with “D” or “d”, json_query sorts the field
- in descending order. Otherwise it sorts the field in ascending order. </p><p> In other cases, the <code class="literal">field_name</code> may be paired with an object
- to specify more details: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[60]</td><td align="right" valign="top" width="10%"><a name="ebnf.sort_class_subdef_1"></a> sort_class_subdef </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{'<br>
- [ '”direction”' ':' lit_value ]<br>
- [ transform_spec ]<br>
- '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table><p> As before, the value tagged as <code class="literal">“direction”</code> specifies the
- direction of the sort, depending on the first character. If not otherwise
- specified, the sort direction defaults to ascending. </p><p> Also as before, the <code class="literal">transform_spec</code> may specify a function
- through which to pass the column. </p><p> Since both the <code class="literal">“direction”</code> tag and the
- <code class="literal">transform_spec</code> are optional, the object may be empty: </p><table width="100%" cellpadding="5" bgcolor="#F5DCB3" border="1" class="productionset" summary="EBNF"><tr><td><table border="0" width="99%" cellpadding="0" bgcolor="#F5DCB3" class="productionset" summary="EBNF productions"><tr><td align="left" valign="top" width="3%">[61]</td><td align="right" valign="top" width="10%"><a name="ebnf.sort_class_subdef_2"></a> sort_class_subdef </td><td valign="top" width="5%" align="center"><code>::=</code></td><td valign="top" width="52%"> '{' '}' </td><td align="left" valign="top" width="30%"> </td></tr></table></td></tr></table></div></div></body></html>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<sect1 version="5.0" xmlns="http://docbook.org/ns/docbook"
- xmlns:xi="http://www.w3.org/2003/XInclude"
- xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <title>Grammar of JSON Queries</title>
-
- <para>
- <author>
- <personname>
- <firstname>Scott</firstname>
- <surname>McKellar</surname>
- </personname>
- <affiliation>
- <orgname>Equinox Software, Inc.</orgname>
- </affiliation>
- </author>
- </para>
-
-
- <sect2>
- <title>Introduction</title>
- <para> The format of this grammar approximates Extended Backus-Naur notation. However it is
- intended as input to human beings, not to parser generators such as Lex or Yacc. Do not
- expect formal rigor. Sometimes narrative text will explain things that are clumsy to
- express in formal notation. More often, the text will restate or summarize the formal
- productions. </para>
- <para> Conventions: </para>
- <orderedlist>
- <listitem>
- <para>The grammar is a series of productions.</para>
- </listitem>
- <listitem>
- <para>A production consists of a name, followed by "::=", followed by a definition
- for the name. The name identifies a grammatical construct that can appear on the
- right side of another production.</para>
- </listitem>
- <listitem>
- <para>Literals (including punctuation) are enclosed in 'single quotes', or in
- "double quotes" if case is not significant.</para>
- </listitem>
- <listitem>
- <para>A single quotation mark within a literal is escaped with a preceding
- backslash: 'dog\'s tail'.</para>
- </listitem>
- <listitem>
- <para>If a construct can be defined more than one way, then the alternatives may
- appear in separate productions; or, they may appear in the same production,
- separated by pipe symbols. The choice between these representations is of only
- cosmetic significance.</para>
- </listitem>
- <listitem>
- <para>A construct enclosed within square brackets is optional.</para>
- </listitem>
- <listitem>
- <para>A construct enclosed within curly braces may be repeated zero or more
- times.</para>
- </listitem>
- <listitem>
- <para>JSON allows arbitrary white space between tokens. To avoid ugly clutter, this
- grammar ignores the optional white space. </para>
- </listitem>
- <listitem>
- <para>In many cases a production defines a JSON object, i.e. a list of name-value
- pairs, separated by commas. Since the order of these name/value pairs is not
- significant, the grammar will not try to show all the possible sequences. In
- general it will present the required pairs first, if any, followed by any
- optional elements.</para>
- </listitem>
- </orderedlist>
-
- <para> Since both EBNF and JSON use curly braces and square brackets, pay close attention to
- whether these characters are in single quotes. If they're in single quotes, they are
- literal elements of the JSON notation. Otherwise they are elements of the EBNF notation.
- </para>
- </sect2>
-
- <sect2>
- <title>Primitives</title>
- <para> We'll start by defining some primitives, to get them out of the way. They're mostly
- just what you would expect. </para>
-
- <productionset>
- <production xml:id="ebnf.string">
- <lhs> string </lhs>
- <rhs> '"' chars '"' </rhs>
- </production>
-
- <production xml:id="ebnf.chars">
- <lhs> chars </lhs>
- <rhs> any valid sequence of UTF-8 characters, with certain special characters
- escaped according to JSON rules </rhs>
- </production>
-
- <production xml:id="ebnf.integer_literal">
- <lhs> integer_literal </lhs>
- <rhs> [ sign ] digit { digit } </rhs>
- </production>
-
- <production xml:id="ebnf.sign">
- <lhs> sign </lhs>
- <rhs> '+' | '-' </rhs>
- </production>
-
- <production xml:id="ebnf.digit">
- <lhs> digit </lhs>
- <rhs>digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'</rhs>
- </production>
-
- <production xml:id="ebnf.integer_string">
- <lhs> integer_string </lhs>
- <rhs> '"' integer_literal '"' </rhs>
- </production>
-
- <production xml:id="ebnf.integer">
- <lhs> integer </lhs>
- <rhs> integer_literal | integer_string </rhs>
- </production>
-
- <production xml:id="ebnf.number">
- <lhs> number </lhs>
- <rhs> any valid character sequence that is numeric according to JSON rules </rhs>
- </production>
-
- </productionset>
-
- <para> When json_query requires an integral value, it will usually accept a quoted string
- and convert it to an integer by brute force – to zero if necessary. Likewise it may
- truncate a floating point number to an integral value. Scientific notation will be
- accepted but may not give the intended results. </para>
-
- <productionset>
-
- <production xml:id="ebnf.boolean">
- <lhs> boolean </lhs>
- <rhs> 'true' | 'false' | string | number </rhs>
- </production>
-
- </productionset>
-
- <para> The preferred way to encode a boolean is with the JSON reserved word true or false,
- in lower case without quotation marks. The string <literal>true</literal>, in upper,
- lower, or mixed case, is another way to encode true. Any other string evaluates to
- false. </para>
- <para> As an accommodation to perl, numbers may be used as booleans. A numeric value of 1
- means true, and any other numeric value means false. </para>
- <para> Any other valid JSON value, such as an array, will be accepted as a boolean but
- interpreted as false. </para>
- <para> The last couple of primitives aren't really very primitive, but we introduce them
- here for convenience: </para>
-
- <productionset>
-
- <production xml:id="ebnf.class_name">
- <lhs> class_name </lhs>
- <rhs> string </rhs>
- </production>
-
- </productionset>
-
- <para> A class_name is a special case of a string: the name of a class as defined by the
- IDL. The class may refer either to a database table or to a source_definition, which is
- a subquery. </para>
-
- <productionset>
-
- <production xml:id="ebnf.field_name">
- <lhs> field_name </lhs>
- <rhs> string </rhs>
- </production>
-
- </productionset>
-
- <para> A field_name is another special case of a string: the name of a non-virtual field as
- defined by the IDL. A field_name is also a column name for the table corresponding to
- the relevant class. </para>
-
- </sect2>
-
- <sect2>
- <title>Query</title>
-
- <para> The following production applies not only to the main query but also to most
- subqueries. </para>
-
- <productionset>
-
- <production xml:id="ebnf.query">
- <lhs> query </lhs>
- <rhs> '{'<sbr/> '"from"' ':' from_list<sbr/> [ ',' '"select"' ':' select_list
- ]<sbr/> [ ',' '"where"' ':' where_condition ]<sbr/> [ ',' '"having"' ':'
- where_condition ]<sbr/> [ ',' '"order_by"' ':' order_by_list ]<sbr/> [ ','
- '"limit"' ':' integer ]<sbr/> [ ',' '"offset"' ':' integer ]<sbr/> [ ','
- '"distinct"' ':' boolean ]<sbr/> [ ',' '"no_i18n"' ':' boolean ]<sbr/> '}'
- </rhs>
- </production>
-
- </productionset>
-
- <para> Except for the <literal>"distinct"</literal> and <literal>no_i18n</literal> entries,
- each name/value pair represents a major clause of the SELECT statement. The name/value
- pairs may appear in any order. </para>
- <para> There is no name/value pair for the GROUP BY clause, because json_query generates it
- automatically according to information encoded elsewhere. </para>
- <para> The <literal>"distinct"</literal> entry, if present and true, tells json_query that
- it may have to create a GROUP BY clause. If not present, it defaults to false. </para>
- <para> The <literal>"no_i18n"</literal> entry, if present and true, tells json_query to
- suppress internationalization. If not present, it defaults to false. (Note that
- <literal>"no_i18n"</literal> contains the digit one, not the letter ell.) </para>
- <para> The values for <literal>limit</literal> and <literal>offset</literal> provide the
- arguments of the LIMIT and OFFSET clauses, respectively, of the SQL statement. Each
- value should be non-negative, if present, or else the SQL won't work. </para>
-
- </sect2>
-
- <sect2><title>FROM Clause</title>
- <para>
- The object identified by <literal>“from”</literal> encodes the FROM clause of
- the SQL. The associated value may be a string, an array, or a JSON object.
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.from_list_0">
- <lhs> from_list </lhs>
- <rhs> class_name </rhs>
- </production>
-
- </productionset>
-
- <para>
- If <literal>from_list</literal> is a <literal>class_name</literal>, the
- json_query inserts the corresponding table name or subquery into the FROM
- clause, using the <literal>class_name</literal> as an alias for the table
- or subquery. The class must be defined as non-virtual in the IDL.
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.from_list_1">
- <lhs> from_list </lhs>
- <rhs> '[' string { ',' parameter } ']' </rhs>
- </production>
-
- <production xml:id="ebnf.parameter">
- <lhs> parameter </lhs>
- <rhs> string | number | 'null' </rhs>
- </production>
-
- </productionset>
-
- <para>
- If from_list is a JSON array, then it represents a table-like function from
- which the SQL statement will select rows, using a SELECT clause consisting
- of “SELECT *” (regardless of the select_list supplied by the method parameter).
- </para>
- <para>
- The first entry in the array is the name of the function. It must be a string
- naming a stored function. Each subsequent entry is a function parameter. If
- it is a string or a number, json_query will insert it into a comma-separated
- parameter list, enclosed in quotes, with any special characters escaped as needed.
- If it is the JSON reserved word <literal>null</literal>, json_query will insert
- it into the parameter list as a null value.
- </para>
- <para>
- If <literal>from_list</literal> is a JSON object, it must contain exactly one entry.
- The key of this entry must be the name of a non-virtual class defined in the IDL.
- This class will be the top-level class of the FROM clause, the only one named
- outside of a JOIN clause.
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.from_list_2">
- <lhs> from_list </lhs>
- <rhs> '{' class_name ':' join_list '}' </rhs>
- </production>
-
- <production xml:id="ebnf.join_list_0">
- <lhs> join_list </lhs>
- <rhs> class_name </rhs>
- </production>
-
- <production xml:id="ebnf.join_list_1">
- <lhs> join_list </lhs>
- <rhs> '{' join_def { ',' join_def } '}' </rhs>
- </production>
-
- </productionset>
-
- <para>
- If the associated data is a <literal>class_name</literal>, json_query will
- construct an INNER JOIN clause joining the class to the top-level clause,
- using the columns specified by the IDL for such a join.
- </para>
- <para>
- Otherwise, the associated data must be a JSON object with one or more entries,
- each entry defining a join:
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.join_def">
- <lhs> join_def </lhs>
- <rhs>
- class_name ':'<sbr/>
- '{'<sbr/>
- [ '”type”' ':' string ]<sbr/>
- [ '”field”' ':' field_name ]<sbr/>
- [ '”fkey”' ':' field_name ]<sbr/>
- [ '”filter”' ':' where_condition ]<sbr/>
- [ '”filter_op”' ':' string ]<sbr/>
- [ '”join”' ':' join_list ]<sbr/>
- '}'
- </rhs>
- </production>
-
- </productionset>
-
- <para>
- The data portion of the <literal>“join_type”</literal> entry tells json_query
- whether to use a left join, right join, full join, or inner join. The values
- <literal>“left”</literal>, <literal>“right”</literal>, and <literal>“full”</literal>,
- in upper, lower, or mixed case, have the obvious meanings. If the
- <literal>“join_type”</literal> entry has any other value, or is not present,
- json_query constructs an inner join.
- </para>
- <para>
- The <literal>“field”</literal> and <literal>“fkey”</literal> attributes specify the
- columns to be equated in the join condition. The <literal>“field”</literal>
- attribute refers to the column in the joined table, i.e. the one named by the
- <literal>join_def</literal>. The <literal>“fkey”</literal> attribute refers to the
- corresponding column in the other table, i.e. the one named outside the
- <literal>join_def</literal> – either the top-level table or a table named by some
- other <literal>join_def</literal>.
- </para>
- <para>
- It may be tempting to suppose that <literal>“fkey”</literal> stands for “foreign key”,
- and therefore refers to a column in the child table that points to the key of a
- parent table. Resist the temptation; the labels are arbitrary. The json_query
- method doesn't care which table is the parent and which is the child.
- </para>
- <para>
- These relationships are best explained with an example. The following
- <literal>from_list</literal>:
- </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "aou": {
- "asv": {
- "type" : "left",
- "fkey" : "id",
- "field" : "owner"
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para>
- ...turns into the following FROM clause:
- </para>
-
- <informalexample>
- <programlisting language="SQL">
- FROM
- actor.org_unit AS "aou"
- LEFT JOIN action.survey AS "asv"
- ON ( "asv".owner = "aou".id )
- </programlisting>
- </informalexample>
-
- <para>
- Note in this example that <literal>“fkey”</literal> refers to a column of the
- class <literal>“aou”</literal>, and <literal>“field”</literal> refers to a
- column of the class <literal>“asv”</literal>.
- </para>
- <para>
- If you specify only one of the two columns, json_query will try to identify the
- other one from the IDL. However, if you specify only the column from the parent
- table, this attempt will probably fail.
- </para>
- <para>
- If you specify both columns, json_query will use the column names you specify,
- without verifying them with a lookup in the IDL. By this means you can perform
- a join using a linkage that the IDL doesn't define. Of course, if the columns
- don't exist in the database, the query will fail when json_query tries to execute it.
- </para>
- <para>
- Using the columns specified, either explicitly or implicitly, the json_query
- method constructs a join condition. With raw SQL it is possible (though
- rarely useful) to join two tables by an inequality. However the json_query
- method always uses a simple equality condition.
- </para>
- <para>
- Using a <literal>“filter”</literal> entry in the join_def, you can apply one
- or more additional conditions to the JOIN clause, typically to restrict the
- join to certain rows of the joined table. The data associated with the
- <literal>“filter”</literal> key is the same sort of
- <literal>where_condition</literal> that you use for a WHERE clause
- (discussed below).
- </para>
- <para>
- If the string associated with the <literal>“filter_op”</literal> entry is
- <literal>“OR”</literal> in upper, lower, or mixed case, then the json_query
- method uses OR to connect the standard join condition to any additional
- conditions supplied by a <literal>“filter”</literal> entry.
- </para>
- <para>
- (Note that if the <literal>where_condition</literal> supplies multiple
- conditions, they will be connected by AND. You will probably want to move
- them down a layer – enclose them in parentheses, in effect – to avoid a
- confusing mixture of ANDs and ORs.)
- </para>
- <para>
- If the <literal>“filter_op”</literal> entry carries any other value, or if
- it is absent, then the json_query method uses AND. In the absence of a
- <literal>“filter”</literal> entry, <literal>“filter_op”</literal> has no effect.
- </para>
- <para>
- A <literal>“join”</literal> entry in a <literal>join_def</literal> specifies
- another layer of join. The class named in the subjoin is joined to the class
- named by the <literal>join_def</literal> to which it is subordinate. By this
- means you can encode multiple joins in a hierarchy.
- </para>
- </sect2>
-
- <sect2><title>SELECT Clause</title>
- <para>
- If a query does not contain an entry for <literal>“select”</literal>, json_query
- will construct a default SELECT clause. The default includes every non-virtual
- field from the top-level class of the FROM clause, as defined by the IDL. The
- result is similar to SELECT *, except:
- </para>
-
- <itemizedlist>
- <listitem>
- <para>The default includes only the fields defined in the IDL.</para>
- </listitem>
- <listitem>
- <para>The columns will appear in the same order in which they appear in the IDL,
- regardless of the order in which the database defines them.</para>
- </listitem>
- </itemizedlist>
-
- <para>
- There are other ways to specify a default SELECT list, as shown below.
- </para>
- <para>
- If a <literal>"select"</literal> entry is present, the associated value must
- be a JSON object, keyed on class names:
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.select_list">
- <lhs> select_list </lhs>
- <rhs> '{' class_name ':' field_list { ',' class_name ':' field_list } '}' </rhs>
- </production>
-
- </productionset>
-
- <para>
- The <literal>class_name</literal> must identify either the top-level class or
- a class belonging to one of the joins. Otherwise json_query will silently
- ignore the <literal>select_list</literal>.
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.field_list_0">
- <lhs> field_list </lhs>
- <rhs> 'null' | '”*”' </rhs>
- </production>
-
- </productionset>
-
- <para>
- If a field_list is either the JSON reserved word <literal>null</literal>
- (in lower case) or an asterisk in double quotes, json_query constructs a
- default SELECT list – provided that the class is the top-level class of the
- query. If the class belongs to a join somewhere, json_query ignores the
- <literal>field_list</literal>.
- </para>
- <para>
- More commonly, the <literal>field_list</literal> is a JSON array of zero or
- more field specifications:
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.field_list_1">
- <lhs> field_list </lhs>
- <rhs> '[' [ field_spec { ',' field_spec } ] ']' </rhs>
- </production>
-
- </productionset>
-
- <para>
- If the array is empty, json_query will construct a default SELECT list for
- the class – again, provided that the class is the top-level class in the query.
- </para>
- <para>
- In the simplest case, a field specification may name a non-virtual field
- defined in the IDL:
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.field_spec_0">
- <lhs> field_spec </lhs>
- <rhs> field_name </rhs>
- </production>
-
- </productionset>
-
- <para>
- In some cases json_query constructs a call to the
- <literal>oils_i18n_xlate</literal> function to internationalize the value of the
- selected column. Specifically, it does so if all the following are true:
- </para>
-
- <itemizedlist>
- <listitem>
- <para>the settings file defines a locale;</para>
- </listitem>
- <listitem>
- <para>in the field definition for the field in the IDL, the tag
- <literal>“il8n”</literal> is present and true;</para>
- </listitem>
- <listitem>
- <para>the query does <emphasis>not</emphasis> include the
- <literal>"no_il8n"</literal> tag (or includes it with a value of false).</para>
- </listitem>
- </itemizedlist>
-
- <para>
- A field specification may be a JSON object:
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.field_spec_1">
- <lhs> field_spec </lhs>
- <rhs>
- '{'<sbr/>
- '”column”' ':' <sbr/>
- [ ',' '”alias”' ':' string ]<sbr/>
- [ ',' '”aggregate”' ':' boolean ]<sbr/>
- [ ',' transform_spec ]<sbr/>
- '}'
- </rhs>
- </production>
-
- </productionset>
-
- <para>
- The <literal>“column”</literal> entry provides the column name, which must
- be defined as non-virtual in the IDL.
- </para>
- <para>
- The <literal>“alias”</literal> entry provides a column alias. If no alias
- is specified, json_query uses the column name as its own alias.
- </para>
- <para>
- The <literal>“aggregate”</literal> entry has no effect on the SELECT clause
- itself. Rather, it affects the construction of a GROUP BY class. If there
- is an <literal>“aggregate”</literal> entry for any field, then json_query builds
- a GROUP BY clause listing every column that is <emphasis>not</emphasis> tagged
- for aggregation (or that carries an <literal>“aggregate”</literal> entry with
- a value of false). If <emphasis>all</emphasis> columns are tagged for
- aggregation, then json_query omits the GROUP BY clause.
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.transform_spec_0">
- <lhs> transform_spec </lhs>
- <rhs>
- '”transform”' ':' string ]<sbr/>
- [ ',' '”result_field” ':' string ]<sbr/>
- [ ',' '”params” ':' param_list ]
- </rhs>
- </production>
-
- </productionset>
-
- <para>
- When a <literal>transform_spec</literal> is present, json_query selects the
- return value of a function instead of selecting the column directly. The entry
- for <literal>“transform”</literal> provides the name of the function, and the
- column name (as specified by the <literal>“column”</literal> tag), qualified by
- the class name, is the argument to the function. For example, you might use such
- a function to format a date or time, or otherwise transform a column value.
- You might also use an aggregate function such as SUM, COUNT, or MAX (possibly
- together with the <literal>“aggregate”</literal> tag).
- </para>
- <para>
- The <literal>“result_field”</literal> entry, when present, specifies a subcolumn
- of the function's return value. The resulting SQL encloses the function call
- in parentheses, and follows it with a period and the subcolumn name.
- </para>
- <para>
- The <literal>“params”</literal> entry, if present, provides a possibly empty
- array of additional parameter values, either strings, numbers, or nulls:
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.param_list">
- <lhs> param_list </lhs>
- <rhs> '[' [ parameter { ',' parameter } ] ']' </rhs>
- </production>
-
- </productionset>
-
- <para>
- Such parameter values are enclosed in single quotes, with any special characters
- escaped as needed, and inserted after the column name as additional parameters
- to the function. You might, for example, use an additional parameter to provide
- a format string for a reformatting function.
- </para>
- </sect2>
-
- <sect2><title>WHERE Clause</title>
- <para> There are two types of <literal>where_condition</literal>: objects and arrays.
- Of these, the object type is the more fundamental, and occurs at some level in every
- <literal>where_condition</literal>. The array type is mainly a way of circumventing
- a limitation of the object type. </para>
- <para> The object type of <literal>where_condition</literal> is a comma-separated list
- of one or more <literal>conditions</literal>: </para>
-
- <productionset>
-
- <production xml:id="ebnf.where_condition_0">
- <lhs> where_condition </lhs>
- <rhs> '{' condition { ',' condition } '}' </rhs>
- </production>
-
- </productionset>
-
- <para> The generated SQL will include a code fragment for each <literal>condition</literal>,
- joined by AND (or in some cases by OR, as described below). </para>
- <para> As usual for entries in a JSON object, each <literal>condition</literal> consists
- of a unique string to serve as a key, a colon, and an associated value. </para>
- <para> The key string may be the name of a column belonging to the relevant table, or
- it may be an operator string. In order to distinguish it from any possible column
- name, an operator string always begins with a plus sign or minus sign. </para>
- <para> JSON requires that every key string be unique within an object. This requirement
- imposes some awkward limitations on a JSON query. For example, you might want to
- express two conditions for the same column: id > 10 and id != 25. Since each of
- those conditions would have the same key string, namely “id”, you can't put them
- into the same JSON object. </para>
- <para> The solution is to put such conflicting conditions in separate JSON objects, and
- put the objects into an array: </para>
-
- <productionset>
-
- <production xml:id="ebnf.where_condition_1">
- <lhs> where_condition </lhs>
- <rhs> '[' where_condition { ',' where_condition } ']' </rhs>
- </production>
-
- </productionset>
-
- <para> The resulting SQL encloses each subordinate set of <literal>conditions</literal>
- in parentheses, and connects the sets with AND (or in some cases OR, as described
- below). It's possible to put only a single <literal>where_condition</literal> in
- the array; the result is to add a layer of parentheses around the condition. </para>
- <para> There are two kinds of <literal>condition</literal> where the operator begins
- with a plus sign. In the simpler case, the associated data is simply a column name:
- </para>
-
- <productionset>
-
- <production xml:id="ebnf.condition_0">
- <lhs> condition </lhs>
- <rhs> plus_class ':' field_name </rhs>
- </production>
-
- </productionset>
-
- <para> A <literal>plus_class</literal> is a string that begins with a plus sign.
- The rest of the string, after the plus sign, must be the class name for the table
- to which the column belongs. </para>
- <para> If the column is a boolean, then the resulting SQL uses it (as qualified by the
- class name) as a stand-alone condition. </para>
- <para> Otherwise, this kind of syntax provides a way to place a column on the right side
- of a comparison operator. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "id": {
- ">": { "+aou":"parent_ou" }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The resulting SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- (
- "aou".id > ( "aou".parent_ou )
- );
- </programlisting>
- </informalexample>
-
- <para> The other type of <literal>condition</literal> that uses a
- <literal>plus_class</literal> applies a specified class name to a
- <literal>where_condition</literal>: </para>
-
- <productionset>
-
- <production xml:id="ebnf.condition_1">
- <lhs> condition </lhs>
- <rhs> plus_class ':' where_condition </rhs>
- </production>
-
- </productionset>
-
- <para> The resulting SQL is enclosed in parentheses, and qualifies the columns with
- the specified class name. This syntax provides a mechanism to shift the class
- context – i.e. to refer to one class in a context that would otherwise refer to
- a different class. </para>
- <para> Ordinarily the class name must be a valid non-virtual class defined in the IDL,
- and applicable to the associated <literal>where_condition</literal>. There is at
- least one peculiar exception. The JSON fragment: </para>
-
- <informalexample>
- <programlisting language="JSON">
- "+abc": { "+xyz":"frobozz" }
- </programlisting>
- </informalexample>
-
- <para> ...is rendered as: </para>
-
- <informalexample>
- <programlisting language="SQL">
- ( "xyz".frobozz )
- </programlisting>
- </informalexample>
-
- <para> ...even though neither <literal>“abc”</literal>, nor <literal>“xyz”</literal>,
- nor <literal>“frobozz”</literal> is defined in the IDL. The class name
- <literal>“abc”</literal> isn't used at all because the <literal>“+xyz”</literal>
- operator overrides it. Such a query won't fail until json_query tries
- to execute it in the database. </para>
- <para> The other operators that may occur at this level all begin with a minus sign,
- and they all represent familiar SQL operators. For example, the
- <literal>“-or”</literal> operator joins the conditions within a
- <literal>where_condition</literal> by OR (instead of the default AND), and
- encloses them all in parentheses: </para>
-
- <productionset>
-
- <production xml:id="ebnf.condition_2">
- <lhs> condition </lhs>
- <rhs> '”-or”' ':' where_condition </rhs>
- </production>
-
- </productionset>
-
- <para> In fact the <literal>“-or”</literal> operator is the only way to get OR into
- the WHERE clause. </para>
- <para> The <literal>“-and”</literal> operator is similar, except that it uses AND: </para>
-
- <productionset>
-
- <production xml:id="ebnf.condition_3">
- <lhs> condition </lhs>
- <rhs> '”-and”' ':' where_condition </rhs>
- </production>
-
- </productionset>
-
- <para> Arguably the <literal>“-and”</literal> operator is redundant, because you can
- get the same effect by wrapping the subordinate <literal>where_condition</literal>
- in a JSON array. Either technique merely adds a layer of parentheses, since AND
- connects successive conditions by default. </para>
- <para> The <literal>“-not”</literal> operator expands the subordinate
- <literal>where_condition</literal> within parentheses, and prefaces the result
- with NOT: </para>
-
- <productionset>
-
- <production xml:id="ebnf.condition_4">
- <lhs> condition </lhs>
- <rhs> '”-not”' ':' where_condition </rhs>
- </production>
-
- </productionset>
-
- <para> The <literal>“-exists”</literal> or <literal>“-not-exists”</literal> operator
- constructs a subquery within an EXISTS or NOT EXISTS clause, respectively: </para>
-
- <productionset>
-
- <production xml:id="ebnf.condition_5">
- <lhs> condition </lhs>
- <rhs> '”-exists”' ':' query </rhs>
- </production>
-
- <production xml:id="ebnf.condition_6">
- <lhs> condition </lhs>
- <rhs> '”-not-exists”' ':' query </rhs>
- </production>
-
- </productionset>
-
- <para> The remaining kinds of <literal>condition</literal> all have a
- <literal>field_name</literal> on the left and some kind of <literal>predicate</literal>
- on the right. A <literal>predicate</literal> places a constraint on the value of
- the column – or, in some cases, on the value of the column as transformed by some
- function call: </para>
-
- <productionset>
-
- <production xml:id="ebnf.condition_7">
- <lhs> condition </lhs>
- <rhs> field_name ':' predicate </rhs>
- </production>
-
- </productionset>
-
- <para> The simplest such constraint is to require that the column have a specific value,
- or be null: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_0">
- <lhs> predicate </lhs>
- <rhs> lit_value | 'null' </rhs>
- </production>
-
- <production xml:id="ebnf.lit_value">
- <lhs> lit_value </lhs>
- <rhs> string | number </rhs>
- </production>
-
- </productionset>
-
- <para> You can also compare a column to a literal value using some kind of inequality.
- However it's a bit more complicated because you have to specify what kind of comparison
- to make: </para>
-
- <productionset>
-
- <production xml:id="ebnf.compare_op_0">
- <lhs> predicate </lhs>
- <rhs> '{' compare_op ':' lit_value '}' </rhs>
- </production>
-
- <production xml:id="ebnf.compare_op_1">
- <lhs> compare_op </lhs>
- <rhs> string </rhs>
- </production>
-
- </productionset>
-
- <para> A <literal>compare_op</literal> is a string that defines a comparison operator.
- Valid values include the following: </para>
-
- <programlisting language="SQL">
- = <> !=
- < > <= >=
- ~ ~* !~ !~*
- like ilike
- similar to
- </programlisting>
-
- <para> Strictly speaking, json_query accepts any <literal>compare_op</literal>
- that doesn't contain semicolons or white space (or
- <literal>“similar to”</literal> as a special exception). As a result, it
- is possible – and potentially useful – to use a custom operator like
- <literal>“>100*”</literal> in order to insert an expression that would
- otherwise be difficult or impossible to create through a JSON query. The ban
- on semicolons and white space prevents certain kinds of SQL injection. </para>
- <para> Note that json_query does <emphasis>not</emphasis> accept two operators that
- PostgreSQL <emphasis>does</emphasis> accept: <literal>“is distinct from”</literal>
- and <literal>“is not distinct from”</literal>. </para>
- <para> You can also compare a column to a null value: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_1">
- <lhs> predicate </lhs>
- <rhs> '{' compare_op ':' 'null' '}' </rhs>
- </production>
-
- </productionset>
-
- <para> The equality operator <literal>“=”</literal> turns into IS NULL. Any other
- operator turns into IS NOT NULL. </para>
- <para> When a <literal>compare_op</literal> is paired with an array, it defines a
- function call: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_2">
- <lhs> predicate </lhs>
- <rhs> '{' compare_op ':' '[' string { ',' parameter } ']' '}' </rhs>
- </production>
-
- </productionset>
-
- <para> The first entry in the array is the function's name. Subsequent entries in
- the array, if any, represent the parameters of the function call. They may be
- strings, numbers, or nulls. In the generated SQL, the function call appears on
- the right of the comparison. </para>
- <para> The <literal>“between”</literal> operator creates a BETWEEN clause: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_3">
- <lhs> predicate </lhs>
- <rhs> '{' “between” ':' '[' lit_value ',' lit_value ']' '}' </rhs>
- </production>
-
- </productionset>
-
- <para> Although PostgreSQL allows a null value in a BETWEEN clause, json_query
- requires literal non-null values. It isn't sensible to use null values in a
- BETWEEN clause. A few experiments show that the results of the comparison are
- peculiar and erratic. </para>
- <para> There are two ways to create an IN list of allowed values. The simplest is
- to put literal values into a JSON array: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_4">
- <lhs> predicate </lhs>
- <rhs> '[' lit_value { ',' lit_value } ']' </rhs>
- </production>
-
- </productionset>
-
- <para> As with BETWEEN clauses, json_query does not accept null values in an IN list,
- even though PostgreSQL does allow them. Nulls are not sensible in this context
- because they never match anything. </para>
- <para> </para>
- </sect2>
-
- <sect2><title>Having Clause</title>
- <para>For the HAVING clause, json_query accepts exactly the same syntax as it accepts for
- the WHERE clause.</para>
- <para> The other way to create an IN list is to use an explicit
- <literal>“in”</literal> operator with an array of literal values. This format
- also works for the <literal>“not in”</literal> operator: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_5">
- <lhs> predicate </lhs>
- <rhs> '{' in_operator ';' '[' lit_value [ ',' lit_value ] ']' '}' </rhs>
- </production>
-
- <production xml:id="ebnf.in_operator">
- <lhs> in_operator </lhs>
- <rhs> “in” | “not in” </rhs>
- </production>
-
- </productionset>
-
- <para> Another kind of IN or NOT IN clause uses a subquery instead of a list of
- values: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_6">
- <lhs> predicate </lhs>
- <rhs> '{' in_operator ':' query '}' </rhs>
- </production>
-
- </productionset>
-
- <para> The remaining types of <literal>predicate</literal> can put a function call on
- the left of the comparison, by using a <literal>transform_spec</literal> together
- with a <literal>“value”</literal> tag. The <literal>transform_spec</literal> is
- optional, and if you don't need it, the same SQL would in many cases be easier to
- express by other means. </para>
- <para> The <literal>transform_spec</literal> construct was described earlier in
- connection with the SELECT clause, but here it is again: </para>
-
- <productionset>
-
- <production xml:id="ebnf.transform_spec_1">
- <lhs> transform_spec </lhs>
- <rhs>
- '”transform”' ':' string ]<sbr/>
- [ ',' '”result_field” ':' string ]<sbr/>
- [ ',' '”params” ':' param_list ]
- </rhs>
- </production>
-
- </productionset>
-
- <para> As in the SELECT clause, the <literal>“transform”</literal> string names the
- function. The first parameter is always the column identified by the field_name.
- Additional parameters, if any, appear in the <literal>param_list</literal>. The
- <literal>“result_field”</literal> string, if present, identifies one column of a
- multicolumn return value. </para>
- <para> Here's a second way to compare a value to a literal value (but not to a null
- value): </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_7">
- <lhs> predicate </lhs>
- <rhs> '{' compare_op ':' '{' '”value”' ':' lit_value<sbr/>
- [ transform_spec ] '}' '}' </rhs>
- </production>
-
- </productionset>
-
- <para> ...and a way to compare a value to a boolean expression: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_8">
- <lhs> predicate </lhs>
- <rhs> '{' compare_op ':' '{' '”value”' ':' '{'<sbr/>
- condition { ',' condition } [ transform_spec ] '}' '}' </rhs>
- </production>
-
- </productionset>
-
- <para> The final predicate is another way to put a function call on the right side
- of the comparison: </para>
-
- <productionset>
-
- <production xml:id="ebnf.predicate_9">
- <lhs> predicate </lhs>
- <rhs> '{' compare_op ':' '{' '”value”' ':' '['<sbr/>
- string { ',' parameter } ']' [ transform_spec ] '}' '}' </rhs>
- </production>
-
- </productionset>
-
- <para> This format is available for the sake of consistency, but offers no advantage
- over the simpler version. </para>
- </sect2>
-
- <sect2><title>ORDER BY Clause</title>
- <para> There are two ways to encode an ORDER BY clause: as an array, or as a list.
- Either may be empty, in which case the generated SQL will not include an ORDER BY
- clause: </para>
-
- <productionset>
-
- <production xml:id="ebnf.order_by_list_0">
- <lhs> order_by_list </lhs>
- <rhs> '[' ']' | '{' '}' </rhs>
- </production>
-
- </productionset>
-
- <para> If not empty, the array contains one or more objects, each defining a sort
- field: </para>
-
- <productionset>
-
- <production xml:id="ebnf.order_by_list_1">
- <lhs> order_by_list </lhs>
- <rhs> '{' sort_field_def { ',' sort_field_def } '}' </rhs>
- </production>
-
- <production xml:id="ebnf.sort_field_def">
- <lhs> sort_field_def </lhs>
- <rhs> '{'<sbr/>
- '”class”' ':' class_name<sbr/>
- ',' '”field”' ':' field_name<sbr/>
- [ ',' '”direction”' ':' lit_value ]<sbr/>
- [ ',' transform_spec ]<sbr/>
- '}' </rhs>
- </production>
-
- </productionset>
-
- <para> The <literal>“class”</literal> and <literal>“field”</literal> entries are
- required, and of course the field must belong to the class. Furthermore, at
- least one field from the class must appear in the SELECT clause. </para>
- <para> The <literal>“direction”</literal> entry, if present, specifies whether the
- sort will be ascending or descending for the column in question. If the associated
- value begins with “D” or “d”, the sort will be descending; otherwise the sort will
- be ascending. If the value is a number, it will be treated as a string that does not
- start with “D” or “d”, resulting in an ascending sort. </para>
- <para> In the absence of a <literal>“direction”</literal> entry, the sort will be
- ascending. </para>
- <para> The <literal>transform_spec</literal> works here the same way it works in the
- SELECT clause and the WHERE clause, enabling you to pass the column through a
- transforming function before the sort: </para>
-
- <productionset>
-
- <production xml:id="ebnf.transform_spec_2">
- <lhs> transform_spec </lhs>
- <rhs>
- '”transform”' ':' string ]<sbr/>
- [ ',' '”result_field” ':' string ]<sbr/>
- [ ',' '”params” ':' param_list ]
- </rhs>
- </production>
-
- </productionset>
-
- <para> When the <literal>order_by_list</literal> is an object instead of an array,
- the syntax is less verbose, but also less flexible. The keys for the object are
- class names: </para>
-
- <productionset>
-
- <production xml:id="ebnf.order_by_list_2">
- <lhs> order_by_list </lhs>
- <rhs> '{' class_name ':' sort_class_def<sbr/>
- { ',' class_name ':' sort_class_def } '}' </rhs>
- </production>
-
- </productionset>
-
- <para> Each class must be referenced in the SELECT clause. </para>
- <para> As in the SELECT clause, all the fields for a given class must be grouped
- together. You can't sort by a column from one table, then a column from a second
- table, then another column from the first table. If you need this kind of sort,
- you must encode the ORDER BY clause as an array instead of an object. </para>
- <para> The data associated with a <literal>class_name</literal> may be either an array
- or an object. If an array, it's simply a list of field names, and each field must
- belong to the class: </para>
-
- <productionset>
-
- <production xml:id="ebnf.sort_class_def_0">
- <lhs> sort_class_def </lhs>
- <rhs> '[' field_name { ',' field_name } ']' </rhs>
- </production>
-
- </productionset>
-
- <para> With this syntax, the direction of sorting will always be ascending. </para>
- <para> If the data is an object, the keys are field names, and as usual the fields
- must belong to the class: </para>
-
- <productionset>
-
- <production xml:id="ebnf.sort_class_def_1">
- <lhs> sort_class_def </lhs>
- <rhs> '{' field_name ':' sort_class_subdef<sbr/>
- { ',' field_name ':' sort_class_subdef } '}' </rhs>
- </production>
-
- </productionset>
-
- <para> Since the <literal>field_name</literal> is the key for the object, it cannot
- appear more than once. As a result, some kinds of sorts are not possible with this
- syntax. For example, one might want to sort by UPPER( family_name ), and then by
- family_name with case unchanged, to make sure that “diBona” comes before “Dibona”.
- For situations like this, you must encode the ORDER BY clause as an array rather
- than an object. </para>
- <para> The data associated with each <literal>field_name</literal> may take either of
- two forms. In the simplest case, it's a literal value to specify the direction
- of sorting: </para>
-
- <productionset>
-
- <production xml:id="ebnf.sort_class_subdef_0">
- <lhs> sort_class_subdef </lhs>
- <rhs> lit_value </rhs>
- </production>
-
- </productionset>
-
- <para> If the literal is a string starting with “D” or “d”, json_query sorts the field
- in descending order. Otherwise it sorts the field in ascending order. </para>
- <para> In other cases, the <literal>field_name</literal> may be paired with an object
- to specify more details: </para>
-
- <productionset>
-
- <production xml:id="ebnf.sort_class_subdef_1">
- <lhs> sort_class_subdef </lhs>
- <rhs> '{'<sbr/>
- [ '”direction”' ':' lit_value ]<sbr/>
- [ transform_spec ]<sbr/>
- '}' </rhs>
- </production>
-
- </productionset>
-
- <para> As before, the value tagged as <literal>“direction”</literal> specifies the
- direction of the sort, depending on the first character. If not otherwise
- specified, the sort direction defaults to ascending. </para>
- <para> Also as before, the <literal>transform_spec</literal> may specify a function
- through which to pass the column. </para>
- <para> Since both the <literal>“direction”</literal> tag and the
- <literal>transform_spec</literal> are optional, the object may be empty: </para>
-
- <productionset>
-
- <production xml:id="ebnf.sort_class_subdef_2">
- <lhs> sort_class_subdef </lhs>
- <rhs> '{' '}' </rhs>
- </production>
-
- </productionset>
- </sect2>
-
-</sect1>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<sect1 version="5.0" xmlns="http://docbook.org/ns/docbook"
- xmlns:xi="http://www.w3.org/2003/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <info>
- <title>JSON Queries: A Tutorial</title>
-
- <author>
- <personname>
- <firstname>Scott</firstname>
- <surname>McKellar</surname>
- </personname>
- <affiliation>
- <orgname>Equinox Software, Inc.</orgname>
- </affiliation>
- </author>
-
- <copyright>
- <year>2009</year>
- <holder>Equinox Software, Inc.</holder>
- </copyright>
-
- <releaseinfo>
- Licensing: Creative Commons Attribution-Share Alike 3.0 United States License.
- </releaseinfo>
- </info>
-
- <sect2>
- <title>Introduction</title>
- <para> The json_query facility provides a way for client applications to query the
- database over the network. Instead of constructing its own SQL, the application
- encodes a query in the form of a JSON string and passes it to the json_query service.
- Then the json_query service parses the JSON, constructs and executes the
- corresponding SQL, and returns the results to the client application. </para>
- <para> This arrangement enables the json_query service to act as a gatekeeper, protecting
- the database from potentially damaging SQL commands. In particular, the generated SQL
- is confined to SELECT statements, which will not change the contents of the database. </para>
- <para> In addition, the json_query service sometimes uses its knowledge of the database
- structure to supply column names and join conditions so that the client application
- doesn't have to. </para>
- <para> Nevertheless, the need to encode a query in a JSON string adds complications,
- because the client needs to know how to build the right JSON. JSON queries are also
- somewhat limiting – they can't do all of the things that you can do with raw SQL. </para>
- <para> This tutorial explains what you can do with a JSON query, and how you can do it. </para>
-
- <sect3>
- <title>The IDL</title>
- <para> A JSON query does not refer to tables and columns. Instead, it refers to classes
- and fields, which the IDL maps to the corresponding database entities. </para>
- <para> The IDL (Interface Definition Language) is an XML file, typically
- <filename>/openils/conf/fm_IDL.xml</filename>. It maps each class to a table, view,
- or subquery, and each field to a column. It also includes information about foreign
- key relationships. </para>
- <para> (The IDL also defines virtual classes and virtual fields, which don't correspond
- to database entities. We won't discuss them here, because json_query ignores them.) </para>
- <para> When it first starts up, json_query loads a relevant subset of the IDL into memory.
- Thereafter, it consults its copy of the IDL whenever it needs to know about the
- database structure. It uses the IDL to validate the JSON queries, and to translate
- classes and fields to the corresponding tables and columns. In some cases it uses the
- IDL to supply information that the queries don't provide. </para>
- </sect3>
-
- <sect3>
- <title>Definitions</title>
- <para> References to “SQL” refer to the dialect implemented by PostgreSQL. This tutorial
- assumes that you are already familiar with SQL. </para>
- <para> You should also be familiar with JSON. However it is worth defining a couple of terms
- that have other meanings in other contexts: </para>
-
- <itemizedlist>
-
- <listitem>
- <para> An “object” is a JSON object, i.e. a comma-separated list of name:value pairs,
- enclosed in curly braces, like this:
- <informalexample>
- <programlisting>
- { “a”:”frobozz”, “b”:24, “c”:null }
- </programlisting>
- </informalexample>
- </para>
- </listitem>
-
- <listitem>
- <para> An “array” is a JSON array, i.e. a comma-separated list of values, enclosed
- in square brackets, like this:
- <informalexample>
- <programlisting>
- [ “Goober”, 629, null, false, “glub” ]
- </programlisting>
- </informalexample>
- </para>
- </listitem>
-
- </itemizedlist>
-
- </sect3>
-
- <sect3>
- <title>The Examples</title>
- <para> The test_json_query utility generated the SQL for all of the sample queries in this
- tutorial. Newlines and indentation were then inserted manually for readability. </para>
- <para> All examples involve the actor.org_unit table, sometimes in combination with a
- few related tables. The queries themselves are designed to illustrate the syntax, not
- to do anything useful at the application level. For example, it's not meaningful to
- take the square root of an org_unit id, except to illustrate how to code a function call.
- The examples are like department store mannequins – they have no brains, they're only
- for display. </para>
- </sect3>
-
- </sect2>
-
- <sect2>
- <title>Hello, World!</title>
-
- <para> The simplest kind of query defines nothing but a FROM clause. For example: </para>
- <informalexample>
- <programlisting language="JSON">
-
- {
- "from":"aou"
- }
-
- </programlisting>
- </informalexample>
- <para> In this minimal example we select from only one table. Later we will see how to join
- multiple tables. </para>
- <para> Since we don't supply a WHERE clause, json_query constructs a default WHERE clause for
- us, including all the available columns. The resulting SQL looks like this: </para>
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".billing_address AS "billing_address",
- "aou".holds_address AS "holds_address",
- "aou".id AS "id",
- "aou".ill_address AS "ill_address",
- "aou".mailing_address AS "mailing_address",
- "aou".name AS "name",
- "aou".ou_type AS "ou_type",
- "aou".parent_ou AS "parent_ou",
- "aou".shortname AS "shortname",
- "aou".email AS "email",
- "aou".phone AS "phone",
- "aou".opac_visible AS "opac_visible"
- FROM
- actor.org_unit AS "aou" ;
- </programlisting>
- </informalexample>
-
- <sect3>
- <title>Default SELECT Clauses</title>
- <para> The default SELECT clause includes every column that the IDL defines it as a
- non-virtual field for the class in question. If a column is present in the database
- but not defined in the IDL, json_query doesn't know about it. In the case of the
- example shown above, all the columns are defined in the IDL, so they all show up in
- the default SELECT clause. </para>
- <para> If the FROM clause joins two or more tables, the default SELECT clause includes
- columns only from the core table, not from any of the joined tables. </para>
- <para> The default SELECT clause has almost the same effect as “<literal>SELECT *</literal>”,
- but not exactly. If you were to “<literal>SELECT * from actor.org_unit_type</literal>
- in psql, the output would include all the same columns as in the example above, but not in
- the same order. A default SELECT clause includes the columns in the order in which the IDL
- defines them, which may be different from the order in which the database defines them. </para>
- <para> In practice, the sequencing of columns in the SELECT clause is not significant.
- The result set is returned to the client program in the form of a data structure, which
- the client program can navigate however it chooses. </para>
- </sect3>
-
- <sect3>
- <title>Other Lessons</title>
- <para> There are other ways to get a default SELECT clause. However, default SELECT clauses
- are a distraction at this point, because most of the time you'll specify your own SELECT
- clause explicitly, as we will discuss later. </para>
- <para> Let's consider some more important aspects of this simple example – more important
- because they apply to more complex queries as well. </para>
-
- <itemizedlist>
-
- <listitem>
- <para> The entire JSON query is an object. In this simple case the object includes
- only one entry, for the FROM clause. Typically you'll also have entries for
- the SELECT clause and the WHERE clause, and possibly for HAVING, ORDER BY,
- LIMIT, or OFFSET clauses. There is no separate entry for a GROUP BY clause,
- which you can specify by other means. </para>
- </listitem>
-
- <listitem>
- <para> Although all the other entries are optional, you must include an entry for
- the FROM clause. You cannot, for example, do a SELECT USER the way you can in
- psql. </para>
- </listitem>
-
- <listitem>
- <para> Every column is qualified by an alias for the table. This alias is always the
- class name for the table, as defined in the IDL. </para>
- </listitem>
-
- <listitem>
- <para> Every column is aliased with the column name. There is a way to choose a
- different column alias (not shown here). </para>
- </listitem>
-
- </itemizedlist>
-
- </sect3>
- </sect2>
-
- <sect2>
- <title>The SELECT Clause</title>
-
- <para> The following variation also produces a default SELECT clause: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": {
- "aou":"*"
- }
- }
- </programlisting>
- </informalexample>
-
- <para> ...and so does this one: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": {
- "aou":null
- },
- "from":"aou"
- }
- </programlisting>
- </informalexample>
-
- <para> While this syntax may not be terribly useful, it does illustrate the minimal structure
- of a SELECT clause in a JSON query: an entry in the outermost JSON object, with a key of
- <literal>“select”</literal>. The value associated with this key is another JSON object,
- whose keys are class names. </para>
- <para> (These two examples also illustrate another point: unlike SQL, a JSON query doesn't care
- whether the FROM clause or the SELECT clause comes first.) </para>
- <para> Usually you don't want the default SELECT clause. Here's how to select only some of the
- columns: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": {
- "aou":[ "id", "name" ]
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The value associated with the class name is an array of column names. If you select
- columns from multiple tables (not shown here), you'll need a separate entry for each
- table, and a separate column list for each entry. </para>
- <para> The previous example results in the following SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou" ;
- </programlisting>
- </informalexample>
-
- <sect3>
- <title>Fancier SELECT Clauses</title>
- <para> The previous example featured an array of column names. More generally, it
- featured an array of field specifications, and one kind of field specification
- is a column name. The other kind is a JSON object, with some combination of the
- following keys: </para>
-
- <itemizedlist>
-
- <listitem>
- <para> <literal>“column”</literal> -- the column name (required). </para>
- </listitem>
-
- <listitem>
- <para> <literal>“alias”</literal> -- used to define a column alias, which
- otherwise defaults to the column name. </para>
- </listitem>
-
- <listitem>
- <para> <literal>“aggregate”</literal> -- takes a value of
- <literal>true</literal> or <literal>false.</literal> Don't worry about
- this one yet. It concerns the use of GROUP BY clauses, which we will
- examine later. </para>
- </listitem>
-
- <listitem>
- <para> <literal>“transform”</literal> -- the name of an SQL function to be
- called. </para>
- </listitem>
-
- <listitem>
- <para> <literal>“result_field”</literal> -- used with
- <literal>“transform”</literal>; specifies an output column of a function that
- returns multiple columns at a time. </para>
- </listitem>
-
- <listitem>
- <para> <literal>“params”</literal> -- used with <literal>“transform”</literal>;
- provides a list of parameters for the function. They may be strings, numbers,
- or nulls. </para>
- </listitem>
-
- </itemizedlist>
-
- <para> This example assigns a different column alias: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": {
- "aou": [
- "id",
- { "column":"name", "alias":"org_name" }
- ]
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "org_name"
- FROM
- actor.org_unit AS "aou" ;
- </programlisting>
- </informalexample>
-
- <para> In this case, changing the column alias doesn't accomplish much. But if we
- were joining to the actor.org_unit_type table, which also has a “name” column,
- we could use different aliases to distinguish them. </para>
- <para> The following example uses a function to raise a column to upper case: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": {
- "aou": [
- "id",
- { "column":"name", "transform":"upper" }
- ]
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- upper("aou".name ) AS "name"
- FROM
- actor.org_unit AS "aou" ;
- </programlisting>
- </informalexample>
-
- <para> Here we take a substring of the name, using the <literal>“params”</literal>
- element to pass parameters: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": {
- "aou": [
- "id", {
- "column":"name",
- "transform":"substr",
- "params":[ 3, 5 ]
- }
- ]
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- substr("aou".name,'3','5' ) AS "name"
- FROM
- actor.org_unit AS "aou" ;
- </programlisting>
- </informalexample>
-
- <para> The parameters specified with <literal>“params”</literal> are inserted
- <emphasis>after</emphasis> the applicable column (<literal>“name”</literal> in this
- case), which is always the first parameter. They are always passed as strings,
- i.e. enclosed in quotes, even if the JSON expresses them as numbers. PostgreSQL
- will ordinarily coerce them to the right type. However if the function name is
- overloaded to accept different types, PostgreSQL may invoke a function other than
- the one intended. </para>
- <para> Finally we call a fictitious function <literal>“frobozz”</literal> that returns
- multiple columns, where we want only one of them: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": {
- "aou": [
- "id", {
- "column":"name",
- "transform":"frobozz",
- "result_field":"zamzam"
- }
- ]
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- (frobozz("aou".name ))."zamzam" AS "name"
- FROM
- actor.org_unit AS "aou" ;
- </programlisting>
- </informalexample>
-
- <para> The “frobozz” function doesn't actually exist, but json_query doesn't know
- that. The query won't fail until json_query tries to execute it in the database. </para>
- </sect3>
-
-
- <sect3>
- <title>Things You Can't Do</title>
- <para> You can do some things in a SELECT clause with raw SQL (with psql, for example)
- that you can't do with a JSON query. Some of them matter and some of them don't. </para>
- <para> When you do a JOIN, you can't arrange the selected columns in any arbitrary
- sequence, because all of the columns from a given table must be grouped together.
- This limitation doesn't matter. The results are returned in the form of a data
- structure, which the client program can navigate however it likes. </para>
- <para> You can't select an arbitrary expression, such as
- <literal>“percentage / 100”</literal> or <literal>“last_name || ', ' || first_name”</literal>.
- Most of the time this limitation doesn't matter either, because the client program
- can do these kinds of manipulations for itself. However, function calls may be a problem.
- You can't nest them, and you can't pass more than one column value to them (and it has
- to be the first parameter). </para>
- <para> You can't use a CASE expression. Instead, the client application can do the equivalent
- branching for itself. </para>
- <para> You can't select a subquery. In raw SQL you can do something like the following: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- id,
- name,
- (
- SELECT name
- FROM actor.org_unit_type AS aout
- WHERE aout.id = aou.ou_type
- ) AS type_name
- FROM
- actor.org_unit AS aou;
- </programlisting>
- </informalexample>
-
- <para> This contrived example is not very realistic. Normally you would use a JOIN in this
- case, and that's what you should do in a JSON query. Other cases may not be so easy
- to solve. </para>
- </sect3>
-
- </sect2>
-
- <sect2>
- <title>The WHERE Clause</title>
- <para> Most queries need a WHERE clause, as in this simple example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "parent_ou":"3"
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Like the SELECT clause, the WHERE clause gets its own entry in the top-level object
- of a JSON query. The key is <literal>“where”</literal>, and the associated value is
- either an object (as shown here) or an array (to be discussed a bit later). Each entry
- in the object is a separate condition. </para>
- <para> In this case, we use a special shortcut for expressing an equality condition. The
- column name is on the left of the colon, and the value to which we are equating it is
- on the right. </para>
- <para> Here's the resulting SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".parent_ou = 3;
- </programlisting>
- </informalexample>
-
- <para> Like the SELECT clause, the generated WHERE clause qualifies each column name with
- the alias of the relevant table. </para>
- <para> If you want to compare a column to NULL, put “<literal>null</literal>” (without
- quotation marks) to the right of the colon instead of a literal value. The resulting
- SQL will include <literal>“IS NULL”</literal> instead of an equals sign. </para>
-
- <sect3>
- <title>Other Kinds of Comparisons</title>
- <para> Here's the same query (which generates the same SQL) without the special
- shortcut: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "parent_ou":{ "=":3 }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> We still have an entry whose key is the column name, but this time the
- associated value is another JSON object. It must contain exactly one entry,
- with the comparison operator on the left of the colon, and the value to be
- compared on the right. </para>
- <para> The same syntax works for other kinds of comparison operators.
- For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "parent_ou":{ ">":3 }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> ...turns into: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".parent_ou > 3 ;
- </programlisting>
- </informalexample>
-
- <para> The condition '<literal>“=”:null</literal>' turns into IS NULL. Any other
- operator used with <literal>“null”</literal> turns into IS NOT NULL. </para>
- <para> You can use most of the comparison operators recognized by PostgreSQL: </para>
-
- <programlisting language="SQL">
- = <> !=
- < > <= >=
- ~ ~* !~ !~*
- like ilike
- similar to
- </programlisting>
-
- <para> The only ones you can't use are <literal>“is distinct from”</literal> and
- <literal>“is not distinct from”</literal>. </para>
- </sect3>
-
- <sect3>
- <title>Custom Comparisons</title>
- <para> Here's a dirty little secret: json_query doesn't really pay much attention to the
- operator you supply. It merely checks to make sure that the operator doesn't contain
- any semicolons or white space, in order to prevent certain kinds of SQL injection.
- It also allows <literal>“similar to”</literal> as a special exception. </para>
- <para> As a result, you can slip an operator of your own devising into the SQL, so long as
- it doesn't contain any semicolons or white space, and doesn't create invalid syntax.
- Here's a contrived and rather silly example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "parent_ou":{ "<2+":3 }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> ...which results in the following SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".parent_ou <2+ 3;
- </programlisting>
- </informalexample>
-
- <para> It's hard to come up with a realistic case where this hack would be useful, but it
- could happen. </para>
- </sect3>
-
- <sect3>
- <title>Comparing One Column to Another</title>
- <para> Here's how to put another column on the right hand side of a comparison: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "id": { ">": { "+aou":"parent_ou" } }
- }
- };
- </programlisting>
- </informalexample>
-
- <para> This syntax is similar to the previous examples, except that instead of comparing
- to a literal value, we compare to an object. This object has only a single entry,
- whose key is a table alias preceded by a leading plus sign. The associated value is
- the name of the column. </para>
- <para> Here's the resulting SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- (
- "aou".id > ( "aou".parent_ou )
- );
- </programlisting>
- </informalexample>
-
- <para> The table alias must correspond to the appropriate table. Since json_query doesn't
- validate the choice of alias, it won't detect an invalid alias until it tries to
- execute the query. In this simple example there's only one table to choose from. The
- choice of alias is more important in a subquery or join. </para>
- <para> The leading plus sign, combined with a table alias, can be used in other situations
- to designate the table to which a column belongs. We shall defer a discussion of this
- usage to the section on joins. </para>
- </sect3>
-
- <sect3>
- <title>Testing Boolean Columns</title>
- <para> In SQL, there are several ways to test a boolean column such as
- actor.org_unit.opac_visible. The most obvious way is to compare it to true or false: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- id
- FROM
- actor.org_unit
- WHERE
- opac_visible = true;
- </programlisting>
- </informalexample>
-
- <para> In a JSON query this approach doesn't work. If you try it, the “= true” test will
- turn into IS NULL. Don't do that. Instead, use a leading plus sign, as described in
- the preceding section, to treat the boolean column as a stand-alone condition: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id" ] },
- "where": {
- "+aou":"opac_visible"
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Result: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".opac_visible ;
- </programlisting>
- </informalexample>
-
- <para> If you need to test for falsity, then write a test for truth and negate it with the
- <literal>“-not”</literal> operator. We will discuss the “-not” operator later, but
- here's a preview: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id" ] },
- "where": {
- "-not": {
- "+aou":"opac_visible"
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id"
- FROM
- actor.org_unit AS "aou"
- WHERE
- NOT ( "aou".opac_visible );
- </programlisting>
- </informalexample>
-
- <para> You can also compare a boolean column directly to a more complex condition: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id" ] },
- "where": {
- "opac_visible": {
- "=": { "parent_ou":{ ">":3 } }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Here we compare a boolean column, not to a literal value, but to a boolean expression.
- The resulting SQL looks a little goofy, but it works: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id"
- FROM
- actor.org_unit AS "aou"
- WHERE
- (
- "aou".opac_visible = ( "aou".parent_ou > 3 )
- );
- </programlisting>
- </informalexample>
-
- <para> In this case we compare the boolean column to a single simple condition. However you
- can include additional complications – multiple conditions, IN lists, BETWEEN clauses,
- and other features as described below. </para>
- </sect3>
-
- <sect3>
- <title>Multiple Conditions</title>
- <para> If you need multiple conditions, just add them to the <literal>“where”</literal>
- object, separated by commas: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "parent_ou":{ ">":3 },
- "id":{ "<>":7 }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The generated SQL connects the conditions with AND: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".parent_ou > 3
- AND "aou".id <> 7;
- </programlisting>
- </informalexample>
-
- <para> Later we will see how to use OR instead of AND. </para>
- </sect3>
-
- <sect3>
- <title>Using Arrays</title>
- <para> Here's a puzzler. Suppose you need two conditions for the same column. How do
- you code them in the same WHERE clause? For example, suppose you want something
- like this: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- id,
- name
- FROM
- actor.org_unit
- WHERE
- parent_ou > 3
- AND parent_ou <> 7;
- </programlisting>
- </informalexample>
-
- <para> You might try a WHERE clause like this: </para>
-
- <informalexample>
- <programlisting language="JSON">
- “where”: {
- “parent_ou”:{ “>”:3 },
- “parent_ou”:{ “<>”:7 }
- }
- </programlisting>
- </informalexample>
-
- <para> Nope. Won't work. According to JSON rules, two entries in the same object
- can't have the same key. </para>
- <para> After slapping yourself in the forehead, you try something a little smarter: </para>
-
- <informalexample>
- <programlisting language="JSON">
- “where”: {
- “parent_ou”: {
- “>”:3,
- “<>”:7
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Nice try, but that doesn't work either. Maybe it ought to work – at least it's
- legal JSON – but, no. </para>
- <para> Here's what works: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": [
- { "parent_ou":{ ">":3 } },
- { "parent_ou":{ "<>":7 } }
- ]
- }
- </programlisting>
- </informalexample>
-
- <para> We wrapped the two conditions into two separate JSON objects, and then wrapped
- those objects together into a JSON array. The resulting SQL looks like this: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name
- FROM
- actor.org_unit AS "aou"
- WHERE
- ( "aou".parent_ou > 3 )
- AND
- ( "aou".parent_ou <> 7 );
- </programlisting>
- </informalexample>
-
- <para> That's not quite what we were hoping for, because the extra parentheses are so ugly.
- But they're harmless. This will do. </para>
- <para> If you're in the mood, you can use arrays to as many parentheses as
- you like, even if there is only one condition inside: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where":
- [[[[[[
- {
- "parent_ou":{ ">":3 }
- },
- ]]]]]]
- }
- </programlisting>
- </informalexample>
-
- <para> ...yields: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- ( ( ( ( ( ( "aou".parent_ou > 3 ) ) ) ) ) );
- </programlisting>
- </informalexample>
-
- </sect3>
-
- <sect3>
- <title>How to OR</title>
- <para> By default, json_query combines conditions with AND. When you need OR,
- here's how to do it: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "-or": {
- "id":2,
- "parent_ou":3
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> We use <literal>“-or”</literal> as the key, with the conditions to be ORed in an
- associated object. The leading minus sign is there to make sure that the operator
- isn't confused with a column name. Later we'll see some other operators with leading
- minus signs. In a couple of spots we even use plus signs. </para>
- <para> Here are the results from the above example: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- (
- "aou".id = 2
- OR "aou".parent_ou = 3
- );
- </programlisting>
- </informalexample>
-
- <para> The conditions paired with <literal>“-or”</literal> are linked by OR and enclosed
- in parentheses, </para>
- <para> Here's how to do the same thing using an array, except that it produces an extra
- layer of parentheses: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "-or": [
- { "id":2 },
- { "parent_ou":3 }
- ]
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- (
- ( "aou".id = 2 )
- OR ( "aou".parent_ou = 3 )
- );
- </programlisting>
- </informalexample>
-
- <para> It's possible, though not very useful, to have only a single condition subject to
- the <literal>“-or”</literal> operator. In that case, the condition appears by itself,
- since there's nothing to OR it to. This trick is another way to add an extraneous
- layer of parentheses, </para>
- </sect3>
-
- <sect3>
- <title>Another way to AND</title>
- <para> You can also use the <literal>“-and”</literal> operator. It works just like
- <literal>“-or”</literal>, except that it combines conditions with AND instead of OR.
- Since AND is the default, we don't usually need a separate operator for it, but it's
- available.
- </para>
- <para>
- In rare cases, nothing else will do – you can't include two conditions in the same
- list because of the duplicate key problem, but you can't combine them with arrays
- either. In particular, you might need to combine them within an expression that
- you're comparing to a boolean column (see the subsection above on Testing Boolean
- Columns). </para>
- </sect3>
-
- <sect3>
- <title>Negation with NOT</title>
- <para> The <literal>“-not”</literal> operator negates a condition or set of conditions.
- For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "-not": {
- "id":{ ">":2 },
- "parent_ou":3
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- NOT
- (
- "aou".id > 2
- AND "aou".parent_ou = 3
- );
- </programlisting>
- </informalexample>
-
- <para> In this example we merely negate a combination of two comparisons. However the
- condition to be negated may be as complicated as it needs to be. Anything that can
- be subject to <literal>“where”</literal> can be subject to
- <literal>“-not”</literal>. </para>
- <para> In most cases you can achieve the same result by other means. However the
- <literal>“-not”</literal> operator is the only way to represent NOT BETWEEN
- (to be discussed later). </para>
- </sect3>
-
- <sect3>
- <title>EXISTS with Subqueries</title>
- <para> Two other operators carry a leading minus sign: <literal>“-exists”</literal>
- and its negation <literal>“-not-exists”</literal>. These operators apply to
- subqueries, which have the same format as a full query. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "-exists": {
- "from":"asv",
- "select":{ "asv":[ "id" ] },
- "where": {
- "owner":7
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- EXISTS
- (
- SELECT "asv".id AS "id"
- FROM action.survey AS "asv"
- WHERE "asv".owner = 7
- );
- </programlisting>
- </informalexample>
-
- <para> This kind of subquery is of limited use, because its WHERE clause doesn't
- have anything to do with the main query. It just shuts down the main query
- altogether if it isn't satisfied. </para>
- <para> More typical is a correlated subquery, whose WHERE clause refers to a row
- from the main query. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "-exists": {
- "from":"asv",
- "select":{ "asv":[ "id" ] },
- "where": {
- "owner":{ "=":{ "+aou":"id" }}
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Note the use of <literal>“+aou”</literal> to qualify the id column in the
- inner WHERE clause. </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- EXISTS
- (
- SELECT "asv".id AS "id"
- FROM action.survey AS "asv"
- WHERE ("asv".owner = ( "aou".id ))
- );
- </programlisting>
- </informalexample>
-
- <para> This latter example illustrates the syntax, but in practice, it would
- probably be more natural to use an IN clause with a subquery (to be discussed
- later). </para>
- </sect3>
-
- <sect3>
- <title>BETWEEN Clauses</title>
- <para> Here's how to express a BETWEEN clause: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id" ] },
- "where": {
- "parent_ou": { "between":[ 3, 7 ] }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The value associated with the column name is an object with a single
- entry, whose key is <literal>“between”</literal>. The corresponding
- value is an array with exactly two values, defining the range to be
- tested. </para>
- <para> The range bounds must be either numbers or string literals. Although
- SQL allows them to be null, a null doesn't make sense in this context,
- because a null never matches anything. Consequently json_query doesn't
- allow them. </para>
- <para> The resulting SQL is just what you would expect: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id"
- FROM
- actor.org_unit AS "aou"
- WHERE
- parent_ou BETWEEN '3' AND '7';
- </programlisting>
- </informalexample>
-
- </sect3>
-
- <sect3>
- <title>IN and NOT IN Lists</title>
- <para> There are two ways to code an IN list. One way is simply to include
- the list of values in an array: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "parent_ou": [ 3, 5, 7 ]
- }
- }
- </programlisting>
- </informalexample>
-
- <para> As with a BETWEEN clause, the values in the array must be numbers or
- string literals. Nulls aren't allowed. Here's the resulting SQL, which
- again is just what you would expect: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".parent_ou IN (3, 5, 7);
- </programlisting>
- </informalexample>
-
- <para> The other way is similar to the syntax shown above for a BETWEEN clause,
- except that the array may include any non-zero number of values: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "parent_ou": { "in": [ 3, 5, 7 ] }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> This version results in the same SQL as the first one. </para>
- <para> For a NOT IN list, you can use the latter format, using the
- <literal>“not in”</literal> operator instead of <literal>“in”</literal>.
- Alternatively, you can use either format together with the
- <literal>“-not”</literal> operator. </para>
- </sect3>
-
- <sect3>
- <title>IN and NOT IN Clauses with Subqueries</title>
- <para> For an IN clause with a subquery, the syntax is similar to the second
- of the two formats for an IN list (see the previous subsection). The
- <literal>“in”</literal> or <literal>“not in”</literal> operator is paired,
- not with an array of values, but with an object representing the subquery.
- For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "id": {
- "in": {
- "from":"asv",
- "select":{ "asv":[ "owner" ] },
- "where":{ "name":"Voter Registration" }
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The results: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".id IN
- (
- SELECT
- "asv".owner AS "owner"
- FROM
- action.survey AS "asv"
- WHERE
- "asv".name = 'Voter Registration'
- );
- </programlisting>
- </informalexample>
-
- <para> In SQL the subquery may select multiple columns, but in a JSON query it
- can select only a single column. </para>
- <para> For a NOT IN clause with a subquery, use the <literal>“not in”</literal>
- operator instead of <literal>“in”</literal>. </para>
- </sect3>
-
- <sect3>
- <title>Comparing to a Function</title>
- <para> Here's how to compare a column to a function call: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "id":{ ">":[ "sqrt", 16 ] }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> A comparison operator (<literal>“>”</literal> in this case) is paired
- with an array. The first entry in the array must be a string giving the name
- of the function. The remaining parameters, if any, are the parameters. They
- may be strings, numbers, or nulls. The resulting SQL for this example: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- "aou".id > sqrt( '16' );
- </programlisting>
- </informalexample>
-
- <para> All parameters are passed as quoted strings -- even if, as in this case,
- they are really numbers. </para>
- <para> This syntax is somewhat limited in that the function parameters must be
- constants (hence the use of a silly example). </para>
- </sect3>
-
- <sect3>
- <title>Putting a Function Call on the Left</title>
- <para> In the discussion of the SELECT clause, we saw how you could transform the value
- of a selected column by passing it to a function. In the WHERE clause, you can use
- similar syntax to transform the value of a column before comparing it to something
- else. </para>
- <para> For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "name": {
- "=": {
- "transform":"upper",
- "value":"CARTER BRANCH"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The <literal>“transform”</literal> entry gives the name of the function that we
- will use on the left side of the comparison. The <literal>“value”</literal> entry
- designates the value on the right side of the comparison. </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- upper("aou".name ) = 'CARTER BRANCH' ;
- </programlisting>
- </informalexample>
-
- <para> As in the SELECT clause, you can pass literal values or nulls to the function
- as additional parameters by using an array tagged as
- <literal>“params”</literal>: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "name": {
- "=": {
- "transform":"substr",
- "params":[ 1, 6 ],
- "value":"CARTER"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- substr("aou".name,'1','6' ) = 'CARTER' ;
- </programlisting>
- </informalexample>
-
- <para> The first parameter is always the column name, qualified by the class name,
- followed by any additional parameters (which are always enclosed in quotes even
- if they are numeric). </para>
- <para> As in the SELECT clause: if the function returns multiple columns, you can specify
- the one you want by using a <literal>“result_field”</literal> entry (not shown
- here). </para>
- <para> If you leave out the <literal>“transform”</literal> entry (or misspell it), the
- column name will appear on the left without any function call. This syntax works,
- but it's more complicated than it needs to be. </para>
- </sect3>
-
- <sect3>
- <title>Putting Function Calls on Both Sides</title>
- <para> If you want to compare one function call to another, you can use the same syntax
- shown in the previous subsection – except that the <literal>“value”</literal> entry
- carries an array instead of a literal value. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "id": {
- ">": {
- "transform":"factorial",
- "value":[ "sqrt", 1000 ]
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- factorial("aou".id ) > sqrt( '1000' ) ;
- </programlisting>
- </informalexample>
-
- <para> The format for the right side function is similar to what we saw earlier, in the
- subsection Comparing to a Function. Note that there are two different formats for
- defining function calls: </para>
-
- <itemizedlist>
-
- <listitem>
- <para> For a function call to the left of the comparison, the function name is
- tagged as <literal>“transform”</literal>. The first parameter is always the
- relevant column name; additional parameters, if any, are in an array tagged
- as <literal>“params”</literal>. The entry for
- <literal>“result_field”</literal>, if present, specifies a subcolumn. </para>
- </listitem>
-
- <listitem>
- <para> For a function call to the right of the comparison, the function name is
- the first entry in an array, together with any parameters. There's no way to
- specify a subcolumn. </para>
- </listitem>
-
- </itemizedlist>
-
- </sect3>
-
- <sect3>
- <title>Comparing a Function to a Condition</title>
- <para> So far we have seen two kinds of data for the <literal>“value”</literal> tag. A
- string or number translates to a literal value, and an array translates to a function
- call. The third possibility is a JSON object, which translates to a condition. For
- example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from":"aou",
- "select": { "aou":[ "id", "name" ] },
- "where": {
- "id": {
- "=": {
- "value":{ "parent_ou":{ ">":3 } },
- "transform":"is_prime"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The function tagged as <literal>“transform”</literal> must return boolean, or else
- json_query will generate invalid SQL. The function used here,
- <literal>“is_prime”</literal>, is fictitious. </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- WHERE
- (
- is_prime("aou".id ) = ( "aou".parent_ou > 3 )
- );
- </programlisting>
- </informalexample>
-
- <para> If we left out the <literal>“transform”</literal> entry, json_query would compare
- the column on the left (which would to be boolean) to the condition on the right.
- The results are similar to those for a simpler format described earlier (see the
- subsection Testing Boolean Columns). </para>
- <para> In the example above we compared the boolean to a simple condition. However the
- expression on the right may include multiple conditions, IN lists, subqueries, and
- whatever other complications are necessary. </para>
- </sect3>
-
- <sect3>
- <title>Things You Can't Do</title>
- <para> The WHERE clause is subject to some of the same limitations as the SELECT clause.
- However, in the WHERE clause these limitations are more limiting, because the client
- program can't compensate by doing some of the work for itself. </para>
- <para> You can't use arbitrary expressions in a WHERE condition, such as
- <literal>“WHERE id > parent_ou – 3”</literal>. In some cases you may be able to
- contrive a custom operator in order to fake such an expression. However this mechanism
- is neither very general nor very aesthetic. </para>
- <para> To the right of a comparison operator, all function parameters must be literals or
- null. You can't pass a column value, nor can you nest function calls. </para>
- <para> Likewise you can't include column values or arbitrary expressions in an IN list
- or a BETWEEN clause. </para>
- <para> You can't include null values in an IN list or a BETWEEN list, not that you should
- ever want to. </para>
- <para> As noted earlier: you can't use the comparison operators
- <literal>“is distinct from”</literal> or <literal>“is not distinct from”</literal>. </para>
- <para> Also as noted earlier: a subquery in an IN clause cannot select more than one
- column. </para>
- </sect3>
-
- </sect2>
-
- <sect2>
- <title>JOIN clauses</title>
- <para> Until now, our examples have selected from only one table at a time. As a result,
- the FROM clause has been very simple – just a single string containing the class name of
- the relevant table. </para>
- <para> When the FROM clause joins multiple tables, the corresponding JSON naturally gets more
- complicated. </para>
- <para> SQL provides two ways to define a join. One way is to list both tables in the FROM
- clause, and put the join conditions in the WHERE clause: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- aou.id,
- aout.name
- FROM
- actor.org_unit aou,
- actor.org_unit_type aout
- WHERE
- aout.id = aou.ou_type;
- </programlisting>
- </informalexample>
-
- <para> The other way is to use an explicit JOIN clause: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- aou.id,
- aout.name
- FROM
- actor.org_unit aou
- JOIN actor.org_unit_type aout
- ON ( aout.id = aou.ou_type );
- </programlisting>
- </informalexample>
-
- <para> JSON queries use only the second of these methods. The following example expresses
- the same query in JSON: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aou":"aout"
- }
- }
- </programlisting>
- </informalexample>
-
- <para> First, let's review the SELECT clause. Since it selects rows from two different tables,
- the data for <literal>“select”</literal> includes two entries, one for each table. </para>
- <para> As for the FROM clause, it's no longer just a string. It's a JSON object, with exactly
- one entry. The key of this entry is the class name of the core table, i.e. the table
- named immediately after the FROM keyword. The data associated with this key contains the
- rest of the information about the join. In this simple example, that information consists
- entirely of a string containing the class name of the other table. </para>
- <para> So where is the join condition? </para>
- <para> It's in the IDL. Upon reading the IDL, json_query knows that actor.org_unit has a
- foreign key pointing to actor.org_unit_type, and builds a join condition accordingly: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".name AS "name"
- FROM
- actor.org_unit AS "aou"
- INNER JOIN actor.org_unit_type AS "aout"
- ON ( "aout".id = "aou".ou_type ) ;
- </programlisting>
- </informalexample>
-
- <para> In this case the core table is the child table, and the joined table is the parent table.
- We could just as well have written it the other way around: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aout":"aou"
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".name AS "name"
- FROM
- actor.org_unit_type AS "aout"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".ou_type = "aout".id ) ;
- </programlisting>
- </informalexample>
-
- <sect3>
- <title>Specifying The Join Columns Explicitly</title>
- <para> While it's convenient to let json_query pick the join columns, it doesn't
- always work. </para>
- <para> For example, the actor.org_unit table has four different address ids, for
- four different kinds of addresses. Each of them is a foreign key to the
- actor.org_address table. Json_query can't guess which one you want if you
- don't tell it. </para>
- <para> (Actually it will try to guess. It will pick the first matching link that
- it finds in the IDL, which may or may not be the one you want.) </para>
- <para> Here's how to define exactly which columns you want for the join: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aoa":[ "street1" ] },
- "from": {
- "aou": {
- "aoa": {
- "fkey":"holds_address",
- "field":"id"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Before, the table we were joining was represented merely by its class name.
- Now it's represented by an entry in a JSON object. The key of that entry is the
- class name, and the associated data is another layer of JSON object containing
- the attributes of the join. </para>
- <para> Later we'll encounter other kinds of join attributes. For now, the only
- attributes that we're looking at are the ones that identify the join columns:
- <literal>“fkey”</literal> and <literal>“field”</literal>. The hard part is
- remembering which is which: </para>
-
- <itemizedlist>
-
- <listitem>
- <para> <literal>“fkey”</literal> identifies the join column from the
- <emphasis>left</emphasis> table; </para>
- </listitem>
-
- <listitem>
- <para> <literal>“field”</literal> identifies the join column from the
- <emphasis>right</emphasis> table. </para>
- </listitem>
-
- </itemizedlist>
-
- <para> When there are only two tables involved, the core table is on the left, and
- the non-core table is on the right. In more complex queries neither table may
- be the core table. </para>
- <para> Here is the result of the preceding JSON: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aoa".street1 AS "street1"
- FROM
- actor.org_unit AS "aou"
- INNER JOIN actor.org_address AS "aoa"
- ON ( "aoa".id = "aou".holds_address ) ;
- </programlisting>
- </informalexample>
-
- <para> In this example the child table is on the left and the parent table is on the
- right. We can swap the tables if we swap the join columns as well: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aoa":[ "street1" ] },
- "from": {
- "aoa": {
- "aou": {
- "fkey":"id",
- "field":"holds_address"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aoa".street1 AS "street1"
- FROM
- actor.org_address AS "aoa"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".holds_address = "aoa".id ) ;
- </programlisting>
- </informalexample>
-
- <para> When you specify both of the join columns, json_query assumes that you know
- what you're doing. It doesn't check the IDL to confirm that the join makes sense.
- The burden is on you to avoid absurdities. </para>
- </sect3>
-
- <sect3>
- <title>Specifying Only One Join Column</title>
- <para> We just saw how to specify both ends of a join. It turns out that there's a
- shortcut – most of the time you only need to specify one end. Consider the
- following variation on the previous example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aoa":[ "street1" ] },
- "from": {
- "aoa": {
- "aou": {
- "field":"holds_address"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> ..which results in exactly the same SQL as before. </para>
- <para> Here we specified the join column from the child table, the column that is a
- foreign key pointing to another table. As long as that linkage is defined in the IDL,
- json_query can look it up and figure out what the corresponding column is in the
- parent table. </para>
- <para> However this shortcut doesn't work if you specify only the column in the parent
- table, because it would lead to ambiguities. Suppose we had specified the id column
- of actor.org_address. As noted earlier, there are four different foreign keys from
- actor.org_unit to actor.org_address, and json_query would have no way to guess
- which one we wanted. </para>
- </sect3>
-
- <sect3>
- <title>Joining to Multiple Tables</title>
- <para> So far we have joined only two tables at a time. What if we need to join one
- table to two different tables? </para>
- <para> Here's an example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "depth" ], "aoa":[ "street1" ] },
- "from": {
- "aou": {
- "aout":{},
- "aoa": {
- "fkey":"holds_address"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The first join, to actor.org_unit_type, is simple. We could have specified join
- columns, but we don't have to, because json_query will construct that join on the
- basis of what it finds in the IDL. Having no join attributes to specify, we leave
- that object empty. </para>
- <para> For the second join, to actor.org_address, we have to specify at least the join
- column in the child table, as discussed earlier. We could also have specified the
- join column from the parent table, but we don't have to, so we didn't. </para>
- <para> Here is the resulting SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".depth AS "depth",
- "aoa".street1 AS "street1"
- FROM
- actor.org_unit AS "aou"
- INNER JOIN actor.org_unit_type AS "aout"
- ON ( "aout".id = "aou".ou_type )
- INNER JOIN actor.org_address AS "aoa"
- ON ( "aoa".id = "aou".holds_address ) ;
- </programlisting>
- </informalexample>
-
- <para> Since there can be only one core table, the outermost object in the FROM clause
- can have only one entry, whose key is the class name of the core table. The next
- level has one entry for every table that's joined to the core table. </para>
- </sect3>
-
- <sect3>
- <title>Nested Joins</title>
- <para> Let's look at that last query again. It joins three tables, and the core table
- is the one in the middle. Can we make one of the end tables the core table instead? </para>
- <para> Yes, we can: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "depth" ], "aoa":[ "street1" ] },
- "from": {
- "aoa": {
- "aou": {
- "field":"holds_address",
- "join": {
- "aout":{ "fkey":"ou_type" }
- }
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The <literal>“join”</literal> attribute introduces another level of join. In this
- case <literal>“aou”</literal> is the left table for the nested join, and the right table
- for the original join. Here are the results: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".depth AS "depth",
- "aoa".street1 AS "street1"
- FROM
- actor.org_address AS "aoa"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".holds_address = "aoa".id )
- INNER JOIN actor.org_unit_type AS "aout"
- ON ( "aout".id = "aou".ou_type ) ;
- </programlisting>
- </informalexample>
-
- </sect3>
-
- <sect3>
- <title>Outer Joins</title>
- <para> By default, json_query constructs an inner join. If you need an outer join, you
- can add the join type as an attribute of the join: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aoa":[ "street1" ] },
- "from": {
- "aoa": {
- "aou": {
- "field":"mailing_address",
- "type":"left"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Here we asked for a left outer join. For a right outer join, code
- <literal>“type”:”right”</literal>. For a full outer join, code
- <literal>“type”:”full”</literal>. Any other value for “type” results in an inner
- join, so watch out for typos. A type of <literal>“rihgt”</literal> will give you
- a wrong join instead of a right one. </para>
- <para> Here is the resulting SQL for this example: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aoa".street1 AS "street1"
- FROM
- actor.org_address AS "aoa"
- LEFT JOIN actor.org_unit AS "aou"
- ON ( "aou".mailing_address = "aoa".id ) ;
- </programlisting>
- </informalexample>
-
- </sect3>
-
- <sect3>
- <title>Referring to Joined Tables in the WHERE Clause</title>
- <para> In the WHERE clause of the generated SQL, every column name is qualified by a
- table alias, which is always the corresponding class name. </para>
- <para> If a column belongs to the core table, this qualification happens by default.
- If it belongs to a joined table, the JSON must specify what class name to use for
- an alias. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aout":"aou"
- },
- "where": {
- "+aou":{ "parent_ou":2 }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> Note the peculiar operator <literal>“+aou”</literal> -- a plus sign followed
- by the relevant class name. This operator tells json_query to apply the specified
- class to the condition that follows. The result: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".name AS "name"
- FROM
- actor.org_unit_type AS "aout"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".ou_type = "aout".id )
- WHERE
- ( "aou".parent_ou = 2 );
- </programlisting>
- </informalexample>
-
- <para> The plus-class operator may apply to multiple conditions: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aout":"aou"
- },
- "where": {
- "+aou":{
- "parent_ou":2,
- "id":{ "<":42 }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".name AS "name"
- FROM
- actor.org_unit_type AS "aout"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".ou_type = "aout".id )
- WHERE
- (
- "aou".parent_ou = 2
- AND "aou".id < 42
- );
- </programlisting>
- </informalexample>
-
- <para> For these artificial examples, it would have been simpler to swap the tables,
- so that actor.org_unit is the core table. Then you wouldn't need to go through
- any special gyrations to apply the right table alias. In a more realistic case,
- however, you might need to apply conditions to both tables. Just swapping the
- tables wouldn't solve the problem. </para>
- <para> You can also use a plus-class operator to compare columns from two different
- tables: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aout":"aou"
- },
- "where": {
- "depth": { ">": { "+aou":"parent_ou" } }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".name AS "name"
- FROM
- actor.org_unit_type AS "aout"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".ou_type = "aout".id )
- WHERE
- (
- "aout".depth > ( "aou".parent_ou )
- );
- </programlisting>
- </informalexample>
-
- <para> Please don't expect that query to make any sense. It doesn't. But it
- illustrates the syntax. </para>
- </sect3>
-
- <sect3>
- <title>Join Filters</title>
- <para> While the above approach certainly works, the special syntax needed is goofy
- and awkward. A somewhat cleaner solution is to include a condition in the JOIN
- clause: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aout": {
- "aou": {
- "filter": {
- "parent_ou":2
- }
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id", "aout".name AS "name"
- FROM
- actor.org_unit_type AS "aout"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".ou_type = "aout".id
- AND "aou".parent_ou = 2 ) ;
- </programlisting>
- </informalexample>
-
- <para> By default, json_query uses AND to combine the <literal>“filter”</literal>
- condition with the original join condition. If you need OR, you can use the
- <literal>“filter_op”</literal> attribute to say so: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aout": {
- "aou": {
- "filter": {
- "parent_ou":2
- },
- "filter_op":"or"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".name AS "name"
- FROM
- actor.org_unit_type AS "aout"
- INNER JOIN actor.org_unit AS "aou"
- ON ( "aou".ou_type = "aout".id
- OR "aou".parent_ou = 2 ) ;
- </programlisting>
- </informalexample>
-
- <para> If the data tagged by <literal>“filter_op”</literal> is anything but
- <literal>“or”</literal> (in upper, lower, or mixed case), json_query uses AND
- instead of OR. </para>
- <para> The condition tagged by <literal>“filter”</literal> may be much more complicated.
- In fact it accepts all the same syntax as the WHERE clause. </para>
- <para> Remember, though, that it all gets combined with the the original join condition
- with an AND, or with an OR if you so specify. If you're not careful, the result
- may be a confusing mixture of AND and OR at the same level. </para>
- </sect3>
-
- <sect3>
- <title>Joining to a Subquery</title>
- <para> In SQL you can put a subquery in a FROM clause, and select from it as if it were
- a table. A JSON query has no way to do that directly. The IDL, however, can define
- a class as a subquery instead of as a table. When you SELECT from it, json_query
- inserts the corresponding subquery into the FROM clause. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "iatc":[ "id", "dest", "copy_status" ] },
- "from": "iatc"
- }
- </programlisting>
- </informalexample>
-
- <para> There's nothing special-looking about this JSON, but json_query expands it as
- follows: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "iatc".id AS "id",
- "iatc".dest AS "dest",
- "iatc".copy_status AS "copy_status"
- FROM
- (
- SELECT t.*
- FROM
- action.transit_copy t
- JOIN actor.org_unit AS s
- ON (t.source = s.id)
- JOIN actor.org_unit AS d
- ON (t.dest = d.id)
- WHERE
- s.parent_ou <> d.parent_ou
- ) AS "iatc" ;
- </programlisting>
- </informalexample>
-
- <para> The <literal>“iatc”</literal> class is like a view, except that it's defined in the
- IDL instead of the database. In this case it provides a way to do a join that would
- otherwise be impossible through a JSON query, because it joins the same table in two
- different ways (see the next subsection). </para>
- </sect3>
-
- <sect3>
- <title>Things You Can't Do</title>
- <para> In a JOIN, as with other SQL constructs, there are some things that you can't do with
- a JSON query. </para>
- <para> In particular, you can't specify a table alias, because the table alias is always the
- class name. As a result: </para>
-
- <itemizedlist>
-
- <listitem>
- <para> You can't join a table to itself. For example, you can't join actor.org_unit
- to itself in order to select the name of the parent for every org_unit. </para>
- </listitem>
-
- <listitem>
- <para> You can't join to the same table in more than one way. For example, you can't
- join actor.org_unit to actor.org_address through four different foreign keys, to
- get four kinds of addresses in a single query. </para>
- </listitem>
-
- </itemizedlist>
-
- <para> The only workaround is to perform the join in a view, or in a subquery defined in
- the IDL as described in the previous subsection. </para>
- <para> Some other things, while not impossible, require some ingenuity in the use of join
- filters. </para>
- <para> For example: by default, json_query constructs a join condition using only a single
- pair of corresponding columns. As long as the database is designed accordingly, a
- single pair of columns will normally suffice. If you ever need to join on more than
- one pair of columns, you can use join filters for the extras. </para>
- <para> Likewise, join conditions are normally equalities. In raw SQL it is possible
- (though rarely useful) to base a join on an inequality, or to use a function call in
- a join condition, or to omit any join condition in order to obtain a Cartesian product.
- If necessary, you can devise such unconventional joins by combining the normal join
- conditions with join filters. </para>
- <para> For example, here's how to get a Cartesian product: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": { "aou":[ "id" ], "aout":[ "name" ] },
- "from": {
- "aout": {
- "aou": {
- "filter": {
- "ou_type":{ "<>": { "+aout":"id" } }
- },
- "filter_op":"or"
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aout".name AS "name"
- FROM
- actor.org_unit_type AS "aout"
- INNER JOIN actor.org_unit AS "aou"
- ON
- (
- "aou".ou_type = "aout".id
- OR ("aou".ou_type <> ( "aout".id ))
- ) ;
- </programlisting>
- </informalexample>
-
- <para> Yes, it's ugly, but at least you're not likely to do it by accident. </para>
- </sect3>
- </sect2>
-
- <sect2>
- <title>Selecting from Functions</title>
- <para> In SQL, you can put a function call in the FROM clause. The function may return
- multiple columns and multiple rows. Within the query, the function behaves like a
- table. </para>
- <para> A JSON query can also select from a function: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "from": [ "actor.org_unit_ancestors", 5 ]
- }
- </programlisting>
- </informalexample>
-
- <para> The data associated with <literal>“from”</literal> is an array instead of a string
- or an object. The first element in the array specifies the name of the function.
- Subsequent elements, if any, supply the parameters of the function; they must be
- literal values or nulls. </para>
- <para> Here is the resulting query: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT *
- FROM
- actor.org_unit_ancestors( '5' ) AS "actor.org_unit_ancestors" ;
- </programlisting>
- </informalexample>
-
- <para> In a JSON query this format is very limited, largely because the IDL knows
- nothing about the available functions. You can't join the function to a table or
- to another function. If you try to supply a SELECT list or a WHERE clause,
- json_query will ignore it. The generated query will always select every column,
- via a wild card asterisk, from every row. </para>
- </sect2>
-
- <sect2>
- <title>The ORDER BY Clause</title>
- <para> In most cases you can encode an ORDER BY clause as either an array or an object.
- Let's take a simple example and try it both ways. First the array: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "aou":[ "name" ] },
- "from": "aou",
- "order_by": [
- { "class":"aou", "field":"name" }
- ]
- }
- </programlisting>
- </informalexample>
-
- <para> Now the object: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "aou":[ "name" ] },
- "from": "aou",
- "order_by": {
- "aou":{ "name":{} }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> The results are identical from either version: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- ORDER BY
- "aou".name;
- </programlisting>
- </informalexample>
-
- <para> The array format is more verbose, but as we shall see, it is also more flexible.
- It can do anything the object format can do, plus some things that the object format
- <emphasis>can't</emphasis> do. </para>
-
- <sect3>
- <title>ORDER BY as an Array</title>
- <para> In the array format, each element of the array is an object defining one of the
- sort fields. Each such object must include at least two tags: </para>
-
- <itemizedlist>
-
- <listitem>
- <para> The <literal>“class”</literal> tag provides the name of the class,
- which must be either the core class or a joined class. </para>
- </listitem>
-
- <listitem>
- <para> The <literal>“field”</literal> tag provides the field name, corresponding
- to one of the columns of the class. </para>
- </listitem>
-
- </itemizedlist>
-
- <para> If you want to sort by multiple fields, just include a separate object for each
- field. </para>
- <para> If you want to sort a field in descending order, add a
- <literal>“direction”</literal> tag: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "aou":[ "name" ] },
- "from": "aou",
- "order_by": [
- { "class":"aou", "field":"name", "direction":"desc" }
- ]
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- ORDER BY
- "aou".name DESC;
- </programlisting>
- </informalexample>
-
- <para> The string tagged as <literal>“direction”</literal> can be anything – all that
- matters is the first character. If the string starts with “D” or “d”, the sort
- will be descending. Otherwise it will be ascending. So
- <literal>“diplodocus”</literal> or <literal>“Dioscorides”</literal> will work as
- well as <literal>“desc”</literal>, but <literal>“going down”</literal> means that
- the sort will go up. </para>
- <para> You can also pass a column through some kind of transforming function, much as
- you can in the SELECT and WHERE clauses, using the <literal>“transform”</literal>
- tag. For example, for a case-insensitive sort, you could raise to upper case: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "aou":[ "name" ] },
- "from": "aou",
- "order_by": [
- {
- "class":"aou",
- "field":"name",
- "transform":"upper"
- }
- ]
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- ORDER BY
- upper("aou".name );
- </programlisting>
- </informalexample>
-
- <para> If you need additional parameters for the function, you can use the
- <literal>“params”</literal> tag to pass them: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "aou":[ "name" ] },
- "from": "aou",
- "order_by": [
- {
- "class":"aou",
- "field":"name",
- "transform":"substr",
- "params":[ 1, 8 ]
- }
- ]
- }
- </programlisting>
- </informalexample>
-
- <para> The additional parameters appear as elements in an array. They may be numbers,
- strings, or nulls. </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- ORDER BY
- substr("aou".name,'1','8' );
- </programlisting>
- </informalexample>
-
- <para> As we have seen elsewhere, all literal values are passed as quoted strings,
- even if they are numbers. </para>
- <para> If the function returns multiple columns, you can use the
- <literal>“result_field”</literal> tag to indicate which one you want (not shown). </para>
- </sect3>
-
- <sect3>
- <title>ORDER BY as an Object</title>
- <para> When you encode the ORDER BY clause as an object, the keys of the object are
- class names. Each class must be either the core class or a joined class. The
- data for each class can be either an array or another layer of object. Here's an
- example with one of each: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "aout":"id", "aou":[ "name" ] },
- "from": { "aou":"aout" },
- "order_by": {
- "aout":[ "id" ],
- "aou":{ "name":{ "direction":"desc" } }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> For the <literal>“aout”</literal> class, the associated array is simply a list
- of field names (in this case, just one). Naturally, each field must reside in
- the class with which it is associated. </para>
- <para> However, a list of field names provides no way to specify the direction of
- sorting, or a transforming function. You can add those details only if the
- class name is paired with an object, as in the example for the
- <literal>“aou”</literal> class. The keys for such an object are field names, and
- the associated tags define other details. </para>
- <para> In this example, we use the <literal>“direction”</literal> tag to specify that
- the name field be sorted in descending order. This tag works the same way here as
- described earlier. If the associated string starts with “D” or “d”, the sort will
- be descending; otherwise it will be ascending. </para>
- <para> Here is the resulting SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- INNER JOIN actor.org_unit_type AS "aout"
- ON ( "aout".id = "aou".ou_type )
- ORDER BY
- "aout".id,
- "aou".name DESC;
- </programlisting>
- </informalexample>
-
- <para> You can also use the <literal>“transform</literal>”, <literal>“params”</literal>,
- and <literal>“result_field”</literal> tags to specify the use of a transforming
- function, as described in the previous subsection. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "aou":[ "name", "id" ] },
- "from": "aou",
- "order_by": {
- "aou":{
- "name":{ "transform":"substr", "params":[ 1, 8 ] }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".name AS "name",
- "aou".id AS "id"
- FROM
- actor.org_unit AS "aou"
- ORDER BY
- substr("aou".name,'1','8' );
- </programlisting>
- </informalexample>
-
- </sect3>
-
- <sect3>
- <title>Things You Can't Do</title>
- <para> If you encode the ORDER BY clause as an object, you may encounter a couple of
- restrictions. </para>
- <para> Because the key of such an object is the class name, all the fields from a given
- class must be grouped together. You can't sort by a column from one table, followed
- by a column from another table, followed by a column from the first table. If you
- need such a sort, you must encode the ORDER BY clause in the array format, which
- suffers from no such restrictions. </para>
- <para> For similar reasons, with an ORDER BY clause encoded as an object, you can't
- reference the same column more than once. Although such a sort may seem perverse,
- there are situations where it can be useful, provided that the column is passed to
- a transforming function. </para>
- <para> For example, you might want a case-insensitive sort, except that for any given
- letter you want lower case to sort first. For example, you want “diBona” to sort
- before “Dibona”. Here's a way to do that, coding the ORDER BY clause as an array: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select":{ "au":[ "family_name", "id" ] },
- "from": "au",
- "order_by": [
- { "class":"au", "field":"family_name", "transform":"upper" },
- { "class":"au", "field":"family_name" }
- ]
- }
- </programlisting>
- </informalexample>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "au".family_name AS "family_name",
- "au".id AS "id"
- FROM
- actor.usr AS "au"
- ORDER BY
- upper("au".family_name ),
- "au".family_name;
- </programlisting>
- </informalexample>
-
- <para> Such a sort is not possible where the ORDER BY clause is coded as an object. </para>
- </sect3>
- </sect2>
-
- <sect2>
- <title>The GROUP BY Clause</title>
- <para> A JSON query has no separate construct to define a GROUP BY clause. Instead, the
- necessary information is distributed across the SELECT clause. However, the way it works
- is a bit backwards from what you might expect, so pay attention. </para>
- <para> Here's an example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": {
- "aou": [
- { "column":"parent_ou" },
- { "column":"name", "transform":"max", "aggregate":true }
- ]
- },
- "from": "aou"
- }
- </programlisting>
- </informalexample>
-
- <para> The <literal>“transform”</literal> tag is there just to give us an excuse to do a GROUP
- BY. What's important to notice is the <literal>“aggregate”</literal> tag. </para>
- <para> Here's the resulting SQL: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".parent_ou AS "parent_ou",
- max("aou".name ) AS "name"
- FROM
- actor.org_unit AS "aou"
- GROUP BY
- 1;
- </programlisting>
- </informalexample>
-
- <para> The GROUP BY clause references fields from the SELECT clause by numerical reference,
- instead of by repeating them. Notice that the field it references, parent_ou, is the
- one that <emphasis>doesn't</emphasis> carry the <literal>“aggregate”</literal> tag in
- the JSON. </para>
- <para> Let's state that more generally. The GROUP BY clause includes only the fields that
- do <emphasis>not</emphasis> carry the <literal>“aggregate”</literal> tag (or that carry
- it with a value of false). </para>
- <para> However, that logic applies only when some field somewhere <emphasis>does</emphasis>
- carry the <literal>“aggregate”</literal> tag, with a value of true. If there is no
- <literal>“aggregate”</literal> tag, or it appears only with a value of false, then there
- is no GROUP BY clause. </para>
- <para> If you really want to include every field in the GROUP BY clause, don't use
- <literal>“aggregate”</literal>. Use the <literal>“distinct”</literal> tag, as described
- in the next section. </para>
- </sect2>
-
- <sect2>
- <title>The DISTINCT Clause</title>
- <para> JSON queries don't generate DISTINCT clauses. However, they can generate GROUP
- BY clauses that include every item from the SELECT clause. The effect is the same
- as applying DISTINCT to the entire SELECT clause. </para>
- <para> For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": {
- "aou": [
- "parent_ou",
- "ou_type"
- ]
- },
- "from":"aou",
- "distinct":"true"
- }
- </programlisting>
- </informalexample>
-
- <para> Note the <literal>“distinct”</literal> entry at the top level of the
- query object, with a value of “true”. </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".parent_ou AS "parent_ou",
- "aou".ou_type AS "ou_type"
- FROM
- actor.org_unit AS "aou"
- GROUP BY
- 1, 2;
- </programlisting>
- </informalexample>
-
- <para> The generated GROUP BY clause references every column in the SELECT clause by
- number. </para>
- </sect2>
-
- <sect2>
- <title>The HAVING Clause</title>
- <para> For a HAVING clause, add a <literal>“having”</literal> entry at the top level
- of the query object. For the associated data, you can use all the same syntax
- that you can use for a WHERE clause. </para>
- <para> Here's a simple example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": {
- "aou": [
- "parent_ou", {
- "column":"id",
- "transform":"count",
- "alias":"id_count",
- "aggregate":"true"
- }
- ]
- },
- "from":"aou",
- "having": {
- "id": {
- ">" : {
- "transform":"count",
- "value":6
- }
- }
- }
- }
- </programlisting>
- </informalexample>
-
- <para> We use the “aggregate” tag in the SELECT clause to give us a GROUP BY to go
- with the HAVING. Results: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".parent_ou AS "parent_ou",
- count("aou".id ) AS "id_count"
- FROM
- actor.org_unit AS "aou"
- GROUP BY
- 1
- HAVING
- count("aou".id ) > 6 ;
- </programlisting>
- </informalexample>
-
- <para> In raw SQL we could have referred to “count( 1 )”. But since JSON queries
- cannot encode arbitrary expressions, we applied the count function to a column
- that cannot be null. </para>
- </sect2>
-
- <sect2>
- <title>The LIMIT and OFFSET Clauses</title>
- <para> To add an LIMIT or OFFSET clause, add an entry to the top level of a query
- object. For example: </para>
-
- <informalexample>
- <programlisting language="JSON">
- {
- "select": {
- "aou": [ "id", "name" ]
- },
- "from":"aou",
- "order_by": { "aou":[ "id" ] },
- "offset": 7,
- "limit": 42
- }
- </programlisting>
- </informalexample>
-
- <para> The data associated with <literal>“offset”</literal> and <literal>“limit”</literal>
- may be either a number or a string, but if it's a string, it should have a number
- inside. </para>
- <para> Result: </para>
-
- <informalexample>
- <programlisting language="SQL">
- SELECT
- "aou".id AS "id",
- "aou".name AS "name"
- FROM
- actor.org_unit AS "aou"
- ORDER BY
- "aou".id
- LIMIT 42
- OFFSET 7;
- </programlisting>
- </informalexample>
-
- </sect2>
-
-</sect1>
\ No newline at end of file