Gelly Query Structure

A query in Gelly declares a bunch of expressions and returns them. Gelly queries are very similar to GraphQL queries or SQL SELECT statements -- they submit what data they want and then the platform returns it all in a predictable shape. A Gelly query can select fields from the underlying models, and expressions on those fields to manipulate or aggregate them, as well as control which records are returned.

As of right now, there's currently no way to execute a raw Gelly query in Gadget, but that will change soon.

Selecting fields of models

Queries can select fields from the available models by listing each field between curly brackets (a selection set) from the plural name of that model. For example, this query selects the id and title fields from the first page of a Blog Post model:

gelly
1query {
2 posts {
3 id
4 title
5 }
6}

This query will return a JSON document containing the selected fields for each blog post that looks like this:

json
1{
2 "posts": [
3 { "id": 1, "title": "Hello World" },
4 { "id": 2, "title": "Thoughts on Software Development" }
5 ]
6}

As many fields as needed can be added to the selection set, and a query can select from more than one model at once if needed. This query selects the first page of Blog Post ids, titles, and publish dates, as well as the current user's name and email:

gelly
1query {
2 posts {
3 id
4 title
5 }
6 currentUser {
7 name
8 email
9 }
10}

The underlying Gelly implementation will efficiently query these two models in parallel and return the data for both as a JSON object like this:

json
1{
2 "posts": [
3 { "id": 1, "title": "Example A" },
4 { "id": 2, "title": "Example B" }
5 ],
6 "currentUser": {
7 "name": "Jane McGee",
8 "email": "[email protected]"
9 }
10}

Fields in a selection must be separated by at least one whitespace character like a space, a tab, or a newline. Model fields are selected using the API Identifier for that field, not the field name.

Which fields you select is entirely up to you -- none are mandatory, and none require other fields to be selected in order to be selected themselves. Usually, the requirements for which fields are being selected are driven by the specific product experience you're trying to build. It's generally faster to select only the fields you need for a specific screen or component in an application, so developers will write one Gelly query (or fragment) per screen and only execute it when that screen is visited.

Selecting relationships

Gelly allows related data for each record to be selected alongside the containing record. To select related data, add an inner set of curly brackets (a nested selection set) to a root selection.

For example, this query selects the id and title of the first page of blog posts, as well as the id and commentBody of the first page of comments for each of the posts:

gelly
1query {
2 posts {
3 id
4 title
5 comments {
6 id
7 commentBody
8 }
9 }
10}

This query returns a JSON document where each element of the posts array will have a comments key holding another array of objects with the fields for each comment:

json
1{
2 "posts": [
3 {
4 "id": 1,
5 "title": "Hello World",
6 "comments": [
7 {
8 "id": 1,
9 "commentBody": "Nice post!"
10 },
11 {
12 "id": 2,
13 "commentBody": "Cool blog!"
14 }
15 ]
16 }
17 ]
18}

Relationship fields can be deeply nested, so you can select relationships of relationships. For example, if Blog Post has many Comments, and then Comments has many Comment Upvotes, you can select all three models at the same time like so:

gelly
1query {
2 posts {
3 id
4 title
5 comments {
6 id
7 commentBody
8 commentUpvotes {
9 id
10 createdAt
11 }
12 }
13 }
14}

Expressions

Gelly allows selecting arbitrary expressions of fields to ask the server to transform records before returning results to the client. Transformations can be simple string operations or math, complicated expressions with boolean logic, or aggregations that summarize a whole set of records into one value.

For example, we can select the full name for each record of a Customer model by adding the firstName and lastName fields together.

gelly
1query {
2 customers {
3 id
4 fullName: firstName + " " + lastName
5 }
6}

This query would produce a result like the one on the right.

json
{
"customers": [{ "fullName": "Jane McGee" }, { "fullName": "Joe Schmoe" }]
}

Expressions are selected right alongside other fields and must be separated from each other with whitespace characters. Expressions can refer to other fields of the record in question, as well as use literals like strings and numbers to do useful work. Expressions can be simple literals, which return value the literal's value.

gelly
query {
1
}

json
{
"1": 1
}

Expressions can also be complicated, and call functions or use boolean logic.

gelly
1query {
2 comments {
3 id
4 isSpam: author.markedAsSpammer || contains(lower(title), "spam") || author.spamScore < (endsWith(author.email, "@gmail.com") ? 10 : 5)
5 }
6}

Aliasing fields and expressions

You can name an expression (or rename a field) when selecting it by prefixing the selection with a name and a colon. For example, to select the title field of a Blog Post model but have the resulting JSON blob use the key humanName to store the field's value, you can run this query:

gelly
query {
posts {
humanName: title
}
}

This would produce a JSON result like:

json
1{
2 "posts": [
3 { "humanName": "Hello World!" },
4 { "humanName": "Thoughts on Software Development" }
5 ]
6}

Aliasing works on expressions as well. To select an uppercase version of the title field from the Blog Post model outputted as the screamingTitle key, the expression can be aliased by prefixing the selection with an alias identifier and a colon.

gelly
query {
posts {
screamingTitle: upcase(title)
}
}

This would produce a JSON result like the one on the right.

json
1{
2 "posts": [
3 { "screamingTitle": "HELLO WORLD!" },
4 { "humanName": "THOUGHTS ON SOFTWARE DEVELOPMENT" }
5 ]
6}

If you don't alias an expression, the expression's source Gelly snippet is used as the output field name. For example, querying for upcase(title) will return the uppercase title under a field named upcase(title)

gelly
query {
posts {
upcase(title)
}
}

This would produce a JSON result like the one on the right.

json
1{
2 "posts": [
3 { "upcase(title)": "HELLO WORLD!" },
4 { "upcase(title)": "THOUGHTS ON SOFTWARE DEVELOPMENT" }
5 ]
6}

Ternary expressions

Gelly supports if-then-else statements using the ? ternary expression, similar to JavaScript. Put a boolean expression first followed by a ? symbol, and then two expressions separated by a :. When the boolean expression is true, the first expression is selected, otherwise, the second expression is selected.

For a simple example, we can select the string "yes" if 10 is greater than 5:

gelly
query {
isTenGreaterThanFive: 10 > 5 ? "yes" : "no"
}

Which would produce a result JSON like so:

json
{
"isTenGreaterThanFive": "yes"
}

Ternary expressions can also be used within selections of relations alongside other selections just fine.

gelly
1query {
2 posts {
3 id
4 title
5 recent: createdAt < (now() - interval("60 days")) ? createdAt : "Old"
6 }
7}

Unlike SQL, ternary expressions don't need to return the same data type from the truthy and falsy expressions on either side of the colon.