Language Overview
ql.io's core consists of a domain-specific procedural-style language based on SQL and JSON. The language does not conform to SQL, but it borrows several concepts from it to keep the language familiar, and extends it to be applicable to HTTP APIs.
We use the term "HTTP API" as a generic reference to any HTTP addressable resource or service. Neither the language nor the runtime of ql.io require the resource or service to be RESTful.
This language currently has the following capabilities:
- Mapping HTTP resources into tables using a
create tablestatement for select, update, insert and delete operations. - JSON objects to declare data.
- Assignment of results of statements or objects to variables.
- Projections of assignments.
- Variable references in statements and objects.
- Return statements.
- Routes to execute stored scripts.
A script is a set of statements and comments. Statements must be declared in the order of dependencies.
There is no normative specification of the language yet. For now see railroad diagrams for details of the currently implemented syntax.
Comments are single-line, and start with --.
-- Welcome to ql.io
Assignments are optional for create table statements and scripts that have only one
statement. Otherwise, statements must be terminated with semi-colons. In single-statement scripts,
the return value of the script is implicit as for the script below.
select * from items
Statements can always be split into multiple lines by using carriage return. Line breaks and
other white space are insignificant. Multi-statement scripts must always end with a
return statement.
Assignment of statements and objects allow later statements in the script to refer to those variables.
items = select * from items; return items;
The assignment happens when the right hand side completes. The right hand side can be one of the following:
- A string, number, JSON object, array,
true,false, ornull- these are all values from JSON. selectstatementinsertstatementupdatestatement (not supported yet)deletestatement (not supported yet)
The predicate following a return can also be any of the above. The above script can
be simplified to the following single line script.
return select * from items;
Since return is implicit in single statement scripts, this is the same as the
following:
select * from items
Selections and projections are not limited to tables - they can applied to objects as well.
Try me
persons = [
{"fname": "John","lname": "Doe", "address": {"city": "San Francisco"}},
{"fname": "Jane","lname": "Doe", "address": {"city": "Bend"}}
];
return select woeid as woeid, centroid as centroid from yahoo.woe where
loc in ("{persons.$..city}");
Execution of each statement can result in one or more HTTP requests. The table referenced in the
statement specifies how to make the HTTP request to a resource. Tables are declared using create table statements. A create table
statement provides all the information necessary to send an HTTP request and process the response.
A create table maps each of select, insert,
update and delete statements into HTTP requests. The most common
mapping is to map a select statement to GET.
create table geocoder
on select get from "http://maps.googleapis.com/maps/api/geocode/json?address={address}&sensor=true"
resultset "results";
Since URIs for reads and writes could be different, a create table statement can map
each of select, insert, update, and delete to a
different HTTP resource with a different method.
create table bitly.shorten
on insert get from "http://api.bitly.com/v3/shorten?login={^login}&apiKey={^apikey}&longUrl={^longUrl}&format={format}"
using defaults apikey = "{config.bitly.apikey}", login = "{config.bitly.login}", format = "json"
using patch 'shorten.js'
resultset 'data.url'
on select get from "http://api.bitly.com/v3/expand?login={^login}&apiKey={^apikey}&shortUrl={^shortUrl}&format={format}"
using defaults apikey = "{config.bitly.apikey}", login = "{config.bitly.login}", format = "json"
using patch 'shorten.js'
resultset 'data.expand'
The mapping between statement types and HTTP methods is arbitrary. The language does not impose
any particular mapping between SQL statement types and HTTP methods. For instance, the above script
maps an insert into statement to method GET because bitly's API chose to
use a GET for a write.
Idempotency and safety of a statement depends directly on the HTTP method used by the API.
The language uses select statements to select all (*) or particular
fields from HTTP resources. The naming of fields follow
the JSONPath convention.
select name.first, name.last, address.street from users where zip = 98074
The from clause of a select statement can either refer to a table or an
object. See select statement for complete syntax and examples.
insert into statements are used to add data.
insert into bitly.shorten (longUrl) values ('http://ql.io/docs')
See insert statement for complete syntax and examples.
updateanddeletestatements are not supported yet.
Statements can depend on other statements by way of using variable
references. A variable reference is a JSONPath-style token in curly braces
({token}) in quoted (double or single) strings.
keyword = "ql.io";
web = select * from bing.search where q = "{keyword}";
tweets = select id as id, from_user_name as user_name, text as text
from twitter.search where q = "ql.io";
return {
"keyword": "{keyword}",
"web": "{web}",
"tweets": "{tweets}"
}
This script has four statements - the first one assigns a string value to a variable, and the second and third statements refer to that variable, and assign the results to two variables. The fourth statement refers to all three.
Variable references determine the order of execution of statements in a script.
The language also supports selecting from objects.
person = {
"f": "Jon",
"l": "Doe"
}
return select f from person;
