Gelly Reference
Built-in data types
Gelly supports literals for a variety of built-in data types that you can use in your expressions. The syntax is very similar to JavaScript, so you can usually use the same syntax to declare literals as you might in JS within Gelly snippets.
Integer
s and Number
s are written out as the digits of the number:
query {anInteger: 5aDecimal: 10.5}
Boolean
s are written as true
or false
:
query {yes: trueno: false}
String
s are written with the characters between double quotes, and using \"
to escape quote characters within the string:
query {aString: "foo"withAQuoteInIt: "when she said \"yes\", it was incredible!"}
DateTime
s are written using the timestamp
function which accepts a timestamp string as input, or the date
function which accepts a date string as input. The date string is formatted according to Postgres' timestamp
and date
literal syntax, which is documented in the Postgres docs. If not specified, times are assumed to be in UTC.
query {aTimestamp: timestamp("2021-05-01 15:00:00")anISO8601Date: timestamp("2021-05-01T15:00:00Z")aDate: date("2021-05-01")}
null
s are written as null
. There's no undefined
in Gelly.
Array
s are written between square brackets with commas (,
) separating the elements.
query {someNumbers: [1, 2, 3]someStrings: ["foo", "bar", "baz"]myStuff: [1, "foo", true, null]}
Object
s are written between curly brackets with the keys on the left-hand side of a colon (:
) and commas (,
) separating the elements.
query {anObject: {foo: "bar", baz: true}}
Built-in operators
Gelly has a number of built-in operators for arithmetic, comparison, and boolean logic.
Boolean logic
Prefix a boolean expression with an exclamation mark to inverse the boolean value.
query {!false # returns true}
Test if two booleans are both true with &&
, or if either boolean is true with ||
1query {2 true && true # returns true3 true && false # returns false4 true || false # returns true5 false || false # returns false6}
Equality and inequality
Test if two expressions are equal with two equals signs, like ==
.
1query {2 1 == 1 # returns true3 1 == 2 # returns false4 true == true # returns true5 true == false # returns false6}
Test if two expressions are not equal with !=
.
1query {2 1 != 1 # returns false3 1 != 2 # returns true4 true != true # returns false5 true != false # returns true6}
Numeric ordering
Test how two numbers relate to each other with >
, >=
, <
, and <=
.
query {1 > 1 # returns false1 >= 1 # returns true10 > 1 # returns true}
Arithmetic
Add, subtract, multiply or divide numbers with +
, -
, *
, or /
:
query {1 + 1 # returns 22 * 10 # returns 1010 / 2 # returns 5}
Division in Gelly returns decimal numbers with arbitrary precision. There is no integer division operator. This is different than most SQL engines, which do integer division by default, and ECMAScript, which uses IEEE-754 floating point numbers and thus has limited precision for mathematics.
query {10 / 3 # returns 3.3 repeating}
Use the %
operator for a modulus (integer division remainder).
query {10 % 3 # returns 1}
Boolean functions
isNull(<value:> Any): Boolean!
Returns true if the passed value
is null, and false otherwise.
To check if something is null, you must use the isNull
function, and not do an equality check like == null
. This is because like SQL,
null
in Gelly is a special state of a data type that exists for every data type, and not a value itself. This different than JavaScript
or other statement based languages where null == null
, in Gelly, anything == null
returns null
itself. This is called three-valued
logic and allows folks writing Gelly queries to not have to do constant null checks to make sure that a value is not null before
manipulating or selecting it.
query {isNull(null) # returns trueisNull({apple: "red"}.apple) # returns falseisNull({apple: "red"}.orange) # returns true}
String functions
concat(<string:> [String!]!, delimiter: String): String!
Returns a new string built by stringing each input string
together, optionally with a delimiter
.
query {concat(["Hello", "", "world!"])}
query {posts {details: concat([title, " by ", author.name])}}
query {posts {details: concat(tags, delimiter: ", ")}}
leftSlice(<string:> String!, length: Number): String!
Returns a substring of the input string
, starting from the first (left most) character position and including length
characters after.
query {leftSlice("foobar", length: 3) # returns "foo"}
length(<string:> String!): Integer!
Returns the number of characters in the passed string
.
query {length("foobar") # returns 6}
lower(<string:> String!): String!
Returns the lower-cased version of the input string
.
query {lower("FooBar") # returns "foobar"}
rightSlice(<string:> String!, length: Number): String!
Returns a substring of the input string
, starting from the last (right most) character position and including length
previous characters.
query {rightSlice("foobar", length: 3) # returns "bar"}
slice(<string:> String!, start: Number = 0, length: Number): String!
Returns a substring of the input string
, starting from the start
character position, or the first character if no start
is passed, and going length
characters further, or to the end of the string if length
is not passed.
query {slice("foobar", length: 3) # returns "foo"}
The start
character position is inclusive, so start: 0
would include the first character in the string onwards, or start: 1
would skip the first character but include the second character onwards.
query {slice("foobar", start: 2, length: 3) # returns "oba"}
If the start
argument is provided and the length
argument isn't provided, the remainder of the string from the start
position is returned.
query {slice("foobar", start: 4) # returns "ar"}
upper(<string:> String!): String!
Returns the upper-cased version of the input string.
query {upper("FooBar") # returns "FOOBAR"}
Aggregation functions
The where
argument
All aggregate functions in Gelly take a where
argument which allows for filtering the set of records for passing through the function. The where
argument doesn't filter the returned set of records, or affect other function calls, it only affects the aggregate function to which it is passed. where
is a convenient way of quickly filtering records without having to filter the whole record set, but can be used interchangeably with the where
relational command.
Using where
with the count
function allows counting records that match certain criteria.
query {posts {count(id, where: isPublished)}}
This is equivalent to running count
with a [where]
relational command.
1query {2 posts {3 count(id)4 [where isPublished]5 }6}
Notably, the where
argument can be used to issue multiple aggregate calls at once with different filter conditions without having to do many outer selection sets. The [where]
relational command filters all records before passing them onto the functions, so all aggregate functions in a selection set are given the same, already filtered list of records to aggregate.
1query {2 posts {3 totalCount: count(id)4 publishedCount: count(id, where: isPublished)5 highScoreCount: count(id, where: score > 10)6 bannedCount: count(id, where: author.banned)7 }8}
avg(<number:> Number!, where: Boolean): Number!
Averages the given field number across the aggregated set, optionally filtered to only consider values that pass the where
condition.
1query {2 posts {3 avg(score)4 [group by author.name]5 }6}
To do a filtered average easily, you can use the where
argument to the avg
function.
query {posts {avg(score, where: isPublished)}}
every(where: Boolean): Boolean!
Returns true if all the passed values are true themselves, or false if any of the passed values are falsy.
query {posts {every(published)}}
To check if a subset of records match the passed conditions, use a where
relational command.
1query {2 posts {3 every(score > 10)4 [where published]5 }6}
count(<value:> Any, where: Boolean): Integer!
Counts the number of non-null value
s across the aggregated set, optionally filtered to only consider values which pass the where
condition.
query {posts {count(id)}}
To do a filtered count easily, you can use the where
argument to the count
function.
query {posts {count(id, where: isPublished)}}
count
is also subject to the relational commands for the query, so you can filter a count
aggregate as well as other aggregates at the same time.
1query {2 posts {3 count(id)4 avg(score)5 [where published]6 }7}
max(<number:> Number!, where: Boolean): Number!
Finds the maximum value of the number
argument across the aggregated set, optionally filtered to only consider values which pass the where
condition.
1query {2 posts {3 max(score)4 [group by author.name]5 }6}
To find the maximum value in a subset of records easily, you can use the where
argument to the max
function.
query {posts {max(score, where: isPublished)}}
min(<number:> Number!, where: Boolean): Number!
Finds the minimum value of the number
argument across the aggregated set, optionally filtered to only consider values which pass the where
condition.
1query {2 posts {3 min(score)4 [group by author.name]5 }6}
To find the minimum value in a subset of records easily, you can use the where
argument to the min
function.
query {posts {min(score, where: isPublished)}}
sum(<number:> Number!, where: Boolean): Number!
Adds the value of the number
argument together across the whole aggregated set, optionally adding only values which pass the where
condition.
1query {2 posts {3 sum(voteCount)4 [group by author.name]5 }6}
To sum only the value from a subset of records easily, you can use the where
argument to the sum
function.
query {posts {max(score, where: isPublished)}}
Time functions
date(input: String!): DateTime!
Create a DateTime
object from an input string representing a date (without a time). Uses the Postgres date and time formatting syntax -- see more details in the Postgres docs.
query {date("2021-05-05")}
1query {2 posts {3 id4 publishAt5 [where publishAt > date("2021-05-05")]6 }7}
datePart(part: String!, date: DateTime!): Number!
Retrieve a given component of a DateTime
object, like the year, week, or minutes. Uses the Postgres syntax for the date part, and returns it as a number. The valid parts are century
, day
, decade
, dow
(day of week), doy
(day of year), epoch
, hour
, isodow
, isoyear
, microseconds
, millennium
, milliseconds
, minute
, month
, quarter
, second
, week
, year
.
See more details about date_part in the Postgres docs.
query {datePart("year", date("2021-05-05"))}
1query {2 posts {3 id4 publishDay: datePart("dow", publishAt)5 }6}
dateTrunc(part: String!, date: DateTime!): DateTime!
Rounds down a DateTime
object to closest date part. Uses the Postgres syntax for the date part. The valid parts are microseconds
, milliseconds
, second
, minute
, hour
, day
, week
, month
, quarter
, year
, decade
, century
, millennium
.
See more details about date_trunc in the Postgres docs.
query {dateTrunc("month", now())}
interval(input: String!): Interval!
Create a Interval
object from an input string representing a specific duration of time. Intervals are useful for doing math with dates. interval
uses the Postgres interval formatting syntax -- see more details in the Postgres docs.
query {short: interval("5 minutes")long: interval("1 year")longer: interval("1 year") + interval("6 months")}
1query {2 posts {3 id4 publishAt5 [where publishAt > now() - interval("60 days")]6 }7}
now(): DateTime!
Gets the current system time from the Gelly server.
query {now()}
1query {2 posts {3 id4 publishAt5 [where publishAt > now()]6 }7}
1query {2 posts {3 id4 publishAt5 [where publishAt > now()]6 }7}
timestamp(input: String!): DateTime!
Create a DateTime
object from an input string representing a specific point in time. Uses the Postgres time formatting syntax -- see more details in the Postgres docs. If not specified, times are assumed to be in UTC.
query {timestamp("2021-05-05 10:10:00")}
1query {2 posts {3 id4 publishAt5 [where publishAt > timestamp("2021-05-01T15:00:00Z")]6 }7}
Numeric functions
abs(<number:> Number!): Integer!
Gets the absolute value of a given input number.
query {abs(-10) # returns 10abs(10) # returns 10}
ceil(<number:> Number!): Integer!
Returns the nearest integer greater than or equal to the input number
.
1query {2 ceil(2.5) # returns 33 ceil(3) # returns 34 ceil(-2.5) # returns -25 ceil(-1) # returns -16}
floor(<number:> Number!): Integer!
Returns the nearest integer less than or equal to the input number
.
1query {2 floor(2.5) # returns 23 floor(3) # returns 34 floor(-2.5) # returns -35 floor(-1) # returns -16}
exp(<number:> Number!): Number!
Returns the exponential (the constant e
raised to the given power) of the input number
.
query {exp(1) # returns 2.7182818284590452}
ln(<number:> Number!): Number!
Returns the natural logarithm (the logarithm with base e
) of the input number
.
query {ln(1) # returns 0ln(10) # returns 2.302585092994046}
log(<number:> Number!, base: Number = 10): Number!
Returns the logarithm with base base
of the input number
. The base
defaults to 10
.
query {log(10) # returns 1log(10, base: 2) # returns 0.1505149978319906}
mod
There is no mod
function in Gelly. Instead, use the %
operator.
power(<number:> Number!, exponent: Number!): Number!
Returns the given number
raised to the given exponent
.
query {power(10, 3) # returns 100power(2, 5) # returns 32}
round(<number:> Number!, precision: Number = 0): Number!
Rounds the given fractional number
to the nearest number of decimal places, counted by precision
. Precision defaults to 0, so when not passed round
rounds to an integer number.
1query {2 round(1) # returns 13 round(1.11111) # returns 14 round(1, precision: 2) # returns 15 round(1.11111, precision: 2) # returns 1.116}
sign(<number:> Number!): Number!
Returns 1
if the number
is positive, -1
if the number
is negative, or 0
if the number is 0.
sqrt(<number:> Number!): Number!
Returns the square root of the given number
.
query {sqrt(4) # returns 2sqrt(10) # returns 3.162277660168379}
trunc(<number:> Number!, precision: Number = 0): Number!
Truncates the given fractional number
to the number of decimal places counted by precision
. trunc
doesn't do any rounding, precision is simply discarded, so if you want to round the number, see round
. precision
defaults to 0.
1query {2 trunc(1) # returns 13 trunc(1.11111) # returns 14 trunc(1.111111, precision: 2 # returns 1.115 trunc(1, precision: 2) # returns 16}
random(): Number!
Returns a random number greater than or equal to 0 and less than 1.
Trigonometric functions and constants
acos(<number:> Number!): Number!
Returns the inverse cosine of the input number
given in radians, output in radians.
asin(<number:> Number!): Number!
Returns the inverse sine of the input number
given in radians, output in radians.
atan(<number:> Number!): Number!
Returns the inverse tangent of the input number
given in radians, output in radians.
cos(<number:> Number!): Number!
Returns the cosine of the input number
given in radians, output in radians.
cot(<number:> Number!): Number!
Returns the cotangent of the input number
given in radians, output in radians.
degrees(<radians:> Number!): Number!
Converts the given input radians
in radians to degrees.
pi
Returns the value of pi, accurate to 15 decimal places.
radians(<degrees:> Number!): Number!
Converts the given input degrees
in degrees to radians.
sin(<number:> Number!): Number!
Returns the sine of the input number
given in radians, output in radians.
tan(<number:> Number!): Number!
Returns the tangent of the input number
given in radians, output in radians.
Type conversion functions
cast(<input:> Any!, typeName: String!): Any!
Converts an input expression to a new type if possible. Can fail if the value of the input can't be converted. Expects the destination type's name to be passed as a string literal, and only accepts values from the table of type names below.
Available type names:
Name | Notes |
---|---|
String | |
Number | Will fail if the input can't be automatically converted to a number. Empty strings, or strings with non-numeric characters will fail. |
Boolean | Converts the string "0" or the empty string to false, and any other string to true. |
query {cast(1, type: "String") # returns "1"cast(-42, type: "String") # returns "-42"}
query {cast("1", type: "Number") # returns 1cast("-42", type: "Number") # returns -42cast("apple", type: "Number") # will throw an error as the string can't be converted}
Note: null
values are not changed when casting in Gelly. cast
ing null always returns null.
Other functions
coalesce(<input:> [Any!]!>): Any!
query {coalesce([null, 1, 2]) # returns "1"}
Returns the first non-null value in the array.