Skip to main content
The API uses type for Database and app for Space. See Terminology.
Install the Cricket template into your own workspace to run every example in the Entities section as-is.

Overview

The general shape of an entity query is:
{
  "q/from": <database name> // "fibery/user", "Kanban/Story"
  "q/select": <select>
  "q/where": <where>
  "q/offset": <integer>
  "q/order-by": <order-by>
  "q/limit": <integer> | "q/no-limit"
}
The clauses are directly analogous to SQL’s from, select, where, offset, order by and limit. q/from, q/select and q/limit are required. This section describes a comprehensive example. See the sections below for a more detailed explanation:
  • select Fields
  • filter Entities
  • order Entities
Get info about cricket players born since 1986 ordered by height and age:
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': [
          'fibery/id',
          'fibery/public-id',
          'Cricket/Name',
          'Cricket/Full Name',
          'Cricket/Born',
          'Cricket/Shirt Number',
          'Cricket/Height',
          'Cricket/Retired',

          { 'Cricket/Batting Hand': ['enum/name'],
            'Cricket/Current Team': ['Cricket/Name'],
            'Cricket/Former Teams': { 'q/select': ['Cricket/Name'], 'q/limit': 100 },
            '# of former teams': ['q/count', ['Cricket/Former Teams', 'fibery/id']] }
        ],
        'q/where': ['>=', ['Cricket/Born'], '$birthday'],
        'q/order-by': [
          [['Cricket/Height'], 'q/desc'],
          [['Cricket/Born'], 'q/asc']
        ],
        'q/limit': 3
      },
      params: { '$birthday': '1986-01-01' }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "# of former teams": 1,
      "Cricket/Height": "1.96",
      "Cricket/Former Teams": [
        {
          "Cricket/Name": "Royal Challengers Bangalore"
        }
      ],
      "Cricket/Born": "1990-01-30",
      "fibery/id": "019dcf9a-c5ab-7292-9bdb-8956eea1b532",
      "Cricket/Shirt Number": 56,
      "Cricket/Full Name": "Mitchell Aaron Starc",
      "fibery/public-id": "19",
      "Cricket/Retired": false,
      "Cricket/Current Team": {
        "Cricket/Name": "Kolkata Knight Riders"
      },
      "Cricket/Batting Hand": {
        "enum/name": "Left"
      },
      "Cricket/Name": "Mitchell Starc"
    },
    {
      "# of former teams": 2,
      "Cricket/Height": "1.85",
      "Cricket/Former Teams": [
        {
          "Cricket/Name": "Delhi"
        },
        {
          "Cricket/Name": "Kolkata Knight Riders"
        }
      ],
      "Cricket/Born": "1988-10-14",
      "fibery/id": "019dcf9a-c294-71cc-b740-ccdbd5722322",
      "Cricket/Shirt Number": 32,
      "Cricket/Full Name": "Glenn James Maxwell",
      "fibery/public-id": "9",
      "Cricket/Retired": false,
      "Cricket/Current Team": {
        "Cricket/Name": "Royal Challengers Bangalore"
      },
      "Cricket/Batting Hand": {
        "enum/name": "Right"
      },
      "Cricket/Name": "Glenn Maxwell"
    },
    {
      "# of former teams": 0,
      "Cricket/Height": "1.85",
      "Cricket/Former Teams": [],
      "Cricket/Born": "1994-10-04",
      "fibery/id": "019dcf9a-c420-71d9-b8c3-e103a7a46c1b",
      "Cricket/Shirt Number": 4,
      "Cricket/Full Name": "Aiden Kyle Markram",
      "fibery/public-id": "14",
      "Cricket/Retired": false,
      "Cricket/Current Team": {
        "Cricket/Name": "Sunrisers Hyderabad"
      },
      "Cricket/Batting Hand": {
        "enum/name": "Right"
      },
      "Cricket/Name": "Aiden Markram"
    }
  ]
}
Command parameters
Parameter (required in bold)Description
query.q/fromDatabase name in format space/name, such as Cricket/Player or fibery/user. Note that the case matters.
query.q/selectArray of primitive Fields, entity Field objects, objects with entity collection Fields subqueries and entity collection Field aggregates.
query.q/whereFilter expression represented as an array.
query.q/order-byArray of sorting expressions — sorting by multiple Fields is supported.
query.q/limitHow many Entities to return. Use a bounded value (e.g. 1000) and paginate to read large datasets — see Pagination. "q/no-limit" is supported but discouraged. In sub-queries, use 100; "q/no-limit" is allowed today but planned for deprecation.
query.q/offsetHow many Entities to skip — useful for pagination.
paramsObject of parameters for filter expressions in { "$param": value } format.
Cricket/Player Database used as an example
Field nameField type
fibery/idfibery/uuid
fibery/public-idfibery/text
Cricket/Namefibery/text
Cricket/Full Namefibery/text
Cricket/Bornfibery/date
Cricket/Shirt Numberfibery/int
Cricket/Heightfibery/decimal
Cricket/Retiredfibery/bool
Cricket/Batting Handsingle-select
Cricket/Current Teamentity Field
Cricket/Former Teamsentity collection Field

Select Fields

In this example we demonstrate the select form known as “vector select”. It’s general form is:
<vec-select> = [ <field-name> | <vec-dereference> | <vec-subselect> ]
<field-name> is used for primitive fields (strings, numbers, booleans and the like). For example, to select id and name (both are text fields):
q/select: ["fibery/id", "fibery/name"]
<field-name> won’t work for fields that point to other entities, because it’s not clear what to include as the value. So the query must specify which fields from the referenced entity to include, and that’s what the <vec-dereference> form does. Here, to add the id of the user in created-by, we add the {thisField: [targetFields] selector:
q/select: [
  "fibery/id",
  "fibery/name",
  {"fibery/created-by": ["fibery/id"]}
]
In the example above, fibery/created-by points to at most one user. When we have a field that point to many entities, that is a collection field, we should have a way to filter them. The dereference form is not sufficient and the <vec-subselect> form must be used. The subselect expression is a full query of its own (a subquery). Here we add to each result also a collection of assignees, where for each assignee we select only one field, id:
q/select: [
  "fibery/id",
  "fibery/name",
  {"fibery/created-by": ["fibery/id"]},
  {"user/assignees": {
    "q/select": ["fibery/id"],
    "q/limit": 100
  }}
]
Note that in some cases empty collections are returned as nulls. In the example below we query entities from the Cricket/Player database and select:
  • a primitive Field Cricket/Full Name of primitive type fibery/text
  • a single-select Cricket/Batting Hand
  • a related entity Cricket/Current Team
  • an entity collection Field Cricket/Former Teams
  • an aggregate (the oldest former team year of foundation).
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': [
          'Cricket/Full Name',

          { 'Cricket/Batting Hand': ['fibery/id', 'enum/name'],
            'Cricket/Current Team': ['fibery/id', 'Cricket/Name'],
            'Cricket/Former Teams': {
              'q/select': ['Cricket/Name', 'Cricket/Year Founded'],
              'q/limit': 100
            },
            'oldest former team founded': ['q/min', ['Cricket/Former Teams', 'Cricket/Year Founded']] }
        ],
        'q/limit': 2
      }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "Cricket/Current Team": {
        "Cricket/Name": "Yorkshire",
        "fibery/id": "1b764d2e-d89c-436f-9977-c5a0257576d0"
      },
      "Cricket/Batting Hand": {
        "enum/name": "Right",
        "fibery/id": "cae2bb37-1133-4158-b1cc-ec91e6ffaa24"
      },
      "Cricket/Full Name": "Joseph Edward Root",
      "Cricket/Former Teams": [
        {
          "Cricket/Name": "Delhi",
          "Cricket/Year Founded": 1934
        }
      ],
      "oldest former team founded": 1934
    },
    {
      "Cricket/Current Team": {
        "Cricket/Name": "Yorkshire",
        "fibery/id": "1b764d2e-d89c-436f-9977-c5a0257576d0"
      },
      "Cricket/Batting Hand": {
        "enum/name": "Right",
        "fibery/id": "cae2bb37-1133-4158-b1cc-ec91e6ffaa24"
      },
      "Cricket/Full Name": "Harry Cherrington Brook",
      "Cricket/Former Teams": [
        {
          "Cricket/Name": "Sunrisers Hyderabad",
          "Cricket/Year Founded": 2012
        }
      ],
      "oldest former team founded": 2012
    }
  ]
}

Select aggregates

select-func is a Lisp-style function call
<select-func> = [<func>, <field-path>, ...]

<func> = "q/count" | "q/min" | "q/max" | ...
Available entity collection Field aggregates
  • q/count
  • q/sum
  • q/avg
  • q/min
  • q/max
It doesn’t matter if a Field is populated manually or via a Formula/Lookup — the query stays exactly the same.

Select count

Count is the only aggregate which can be used on the top level:
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': ['q/count', ['fibery/id']]
      }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": 20
}

Filter Entities

Filters (where) go into the q/where clause of the query. The general form is inspired by Lisp: it’s a list where the first element is an operator and the remaining elements are the values to check using the operator:
<where> = [<operator> <operand> <operand> ...]

<operator> = ">", ">=", "<", "<=", ...
<operand> = <fieldexpr> | <subquery-vec>
In these examples we filter by:
  • a primitive Field (height)
  • two primitive Fields (birth date and retirement status)
  • a single-select (batting hand)
  • an entity Field (current team)
  • an entity collection Field (former teams)
We don’t compare Entity’s Field to a value directly, but use a $param instead. Cricket/Player Database used as an example
Field nameField type
Cricket/Heightfibery/decimal
Cricket/Youth Careerfibery/date-range
Cricket/Retiredfibery/bool
Cricket/Batting Handsingle-select
Cricket/Current Teamentity Field
Cricket/Former Teamsentity collection Field
Filter operators by field type
Field typeOperators
Number, Date=, !=, <, <=, >, >=, q/null?
Textq/equals-ignoring-case?, q/not-equals-ignoring-case?, q/contains, q/not-contains, q/starts-with-ignoring-case?, q/ends-with-ignoring-case?, q/null-or-empty?
Boolean / null check["=", ["q/null?", ["FieldPath"]], "$boolParam"]
Referenceq/in, q/not-in, q/count
Date-range start/end["q/start", ["FieldPath"]], ["q/end", ["FieldPath"]]
Location["q/address", ["FieldPath"]] — parses to string, then use text operators
Filter combinators q/and and q/or take multiple filter clauses as operands:
["q/and", ["=", ["FieldPath1"], "$state"], [">", ["FieldPath2"], "$min"]]
Examples Get players taller than 1.75 meters — primitive Field:
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': ['Cricket/Name', 'Cricket/Height'],
        'q/where': ['>', ['Cricket/Height'], '$height'],
        'q/limit': 2
      },
      params: { '$height': '1.75' }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "Cricket/Name": "Joe Root",
      "Cricket/Height": "1.83"
    },
    {
      "Cricket/Name": "Harry Brook",
      "Cricket/Height": "1.80"
    }
  ]
}
Filters can be nested and combined using q/and and q/or. Here we are querying players who started their youth career before 2004 and haven’t retired yet:
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': ['Cricket/Name', 'Cricket/Youth Career', 'Cricket/Retired'],
        'q/where': [
          'q/and',
          ['<', ['q/start', ['Cricket/Youth Career']], '$date'],
          ['=', ['Cricket/Retired'], '$retired?']
        ],
        'q/limit': 2
      },
      params: {
        '$date': '2004-01-01',
        '$retired?': false
      }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "Cricket/Name": "Adam Lyth",
      "Cricket/Youth Career": {
        "start": "2002-01-01",
        "end": "2006-12-31"
      },
      "Cricket/Retired": false
    },
    {
      "Cricket/Name": "Dawid Malan",
      "Cricket/Youth Career": {
        "start": "2002-01-01",
        "end": "2006-12-31"
      },
      "Cricket/Retired": false
    }
  ]
}
Get right-handed players — single-select:
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': ['Cricket/Name', { 'Cricket/Batting Hand': ['enum/name'] }],
        'q/where': ['=', ['Cricket/Batting Hand', 'enum/name'], '$hand'],
        'q/limit': 2
      },
      params: { '$hand': 'Right' }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "Cricket/Name": "Joe Root",
      "Cricket/Batting Hand": {
        "enum/name": "Right"
      }
    },
    {
      "Cricket/Name": "Harry Brook",
      "Cricket/Batting Hand": {
        "enum/name": "Right"
      }
    }
  ]
}
Get players whose current team was founded before 2000 — entity Field:
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': ['Cricket/Name', { 'Cricket/Current Team': ['Cricket/Name'] }],
        'q/where': ['<', ['Cricket/Current Team', 'Cricket/Year Founded'], '$year'],
        'q/limit': 2
      },
      params: { '$year': 2000 }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "Cricket/Name": "Joe Root",
      "Cricket/Current Team": {
        "Cricket/Name": "Yorkshire"
      }
    },
    {
      "Cricket/Name": "Harry Brook",
      "Cricket/Current Team": {
        "Cricket/Name": "Yorkshire"
      }
    }
  ]
}
Get players who previously played for Yorkshire or Derbyshire — entity collection Field:
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': [
          'Cricket/Name',
          { 'Cricket/Former Teams': { 'q/select': ['Cricket/Name'], 'q/limit': 100 } }
        ],
        'q/where': ['q/in', ['Cricket/Former Teams', 'Cricket/Name'], '$teams'],
        'q/limit': 2
      },
      params: { '$teams': ['Yorkshire', 'Derbyshire'] }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "Cricket/Name": "Cheteshwar Pujara",
      "Cricket/Former Teams": [
        {
          "Cricket/Name": "Yorkshire"
        },
        {
          "Cricket/Name": "Royal Challengers Bangalore"
        }
      ]
    },
    {
      "Cricket/Name": "Kane Williamson",
      "Cricket/Former Teams": [
        {
          "Cricket/Name": "Yorkshire"
        },
        {
          "Cricket/Name": "Delhi"
        },
        {
          "Cricket/Name": "Royal Challengers Bangalore"
        }
      ]
    }
  ]
}

Order Entities

<order-by> = [[<field-path>, "q/asc" | "q/desc"], ...];
Sort Entities by multiple primitive and entity Fields. The default sorting is by creation date and UUID: [ [["fibery/creation-date"], "q/asc"], [["fibery/id"], "q/asc"] ] Sorting by fibery/id guarantees that Entities order won’t change on different executions of the same query. It is also the basis of cursor pagination. Cricket/Player Database used as an example
Field nameField type
Cricket/Heightfibery/decimal
Cricket/Current Teamentity Field
const response = await fetch('https://YOUR_ACCOUNT.fibery.io/api/commands', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    command: 'fibery.entity/query',
    args: {
      query: {
        'q/from': 'Cricket/Player',
        'q/select': [
          'Cricket/Name',
          'Cricket/Height',
          { 'Cricket/Current Team': ['Cricket/Name'] }
        ],
        'q/order-by': [
          [['Cricket/Height'], 'q/desc'],
          [['Cricket/Current Team', 'Cricket/Name'], 'q/asc']
        ],
        'q/limit': 3
      }
    }
  })
});
const data = await response.json();
Response:
{
  "success": true,
  "result": [
    {
      "Cricket/Current Team": {
        "Cricket/Name": "Kolkata Knight Riders"
      },
      "Cricket/Height": "1.96",
      "Cricket/Name": "Mitchell Starc"
    },
    {
      "Cricket/Current Team": {
        "Cricket/Name": "Royal Challengers Bangalore"
      },
      "Cricket/Height": "1.85",
      "Cricket/Name": "Glenn Maxwell"
    },
    {
      "Cricket/Current Team": {
        "Cricket/Name": "Sunrisers Hyderabad"
      },
      "Cricket/Height": "1.85",
      "Cricket/Name": "Aiden Markram"
    }
  ]
}