Filtering

API filtering enables precise data retrieval using logical operators like _gt, _lt, _like, _in, and _between. It supports complex queries and comparisons.

Overview

Filtering is a key feature of the API. You can specify the filter as a parameter to a connection. Let’s look at some examples:

In this first example, we fetch all the general ledger accounts that have the account number greater or equal than 6000 and lower than 7000.

Query

query read($cid : Int!)
{  
  useCompany(no: $cid)
  {
    generalLedgerAccount(
      filter: {_and :[
        {accountNo :{_gte : 6000}},
        {accountNo :{_lt : 7000}}
      ]}
    )
    {
      totalCount
      items
      {
        accountNo
        name
      }
    }
  }
}
Result

{
  "data": {
    "useCompany": {
      "generalLedgerAccount": {
        "totalCount": 46,
        "items": [
          {
            "accountNo": 6000,
            "name": "Avskr. bygn. og annen eiendom"
          },
          {
            "accountNo": 6010,
            "name": "Avskr. maskiner, inventar mv."
          },
          ...
        ]
      }
    }
  }
}  

We can complicate the query a bit, and ask only for those general ledger accounts in the range 6000 - 7000 that start with “Leie”. In this case, the filter would look as follows:

Query

query read($cid : Int!)
{  
  useCompany(no: $cid)
  {
    generalLedgerAccount(
      filter: {_and :[
        {accountNo :{_gte : 6000}},
        {accountNo :{_lt : 7000}},
        {name :{_like:"Leie%"}}
      ]}
    )
    {
      totalCount
      items
      {
        accountNo
        name
      }
    }
  }
}
Result

{
  "data": {
    "useCompany": {
      "generalLedgerAccount": {
        "totalCount": 6,
        "items": [
          {
            "accountNo": 6300,
            "name": "Leie lokaler"
          },
          {
            "accountNo": 6400,
            "name": "Leie maskiner"
          },
          {
            "accountNo": 6410,
            "name": "Leie inventar"
          },
          {
            "accountNo": 6420,
            "name": "Leie datasystemer"
          },
          {
            "accountNo": 6430,
            "name": "Leie andre kontormaskiner"
          },
          {
            "accountNo": 6440,
            "name": "Leie transportmidler"
          }
        ]
      }
    }
  }
}

In the next example, we fetch all the associates that are either customers with the number in the range 10000 - 10010, or suppliers, with the number in the range 50000 - 50010.

Query

query read($cid : Int!)
{
  useCompany(no: $cid) {
    associate (
      filter:{
        _or:
        [
           {
            _and : [
               {customerNo :{_gt:10000}},
               {customerNo :{_lt:10010}}
            ]
          },
          {
            _and : [
               {supplierNo : {_gt:50000}},
               {supplierNo : {_lt:50010}},
            ]
          }
        ]
      }
    )
    {
      totalCount
      items {
        name
        customerNo
        supplierNo
      }
    }
  }
}
Result

{
  "data": {
    "useCompany": {
      "associate": {
        "totalCount": 18,
        "items": [
          {
            "name": "YoYo Solutions AS",
            "customerNo": 10001,
            "supplierNo": 0
          },
          {
            "name": "Spire Business",
            "customerNo": 10002,
            "supplierNo": 0
          },
          ...
          {
            "name": "Scorpio Services Limited",
            "customerNo": 0,
            "supplierNo": 50001
          },
          {
            "name": "Atlas Systems",
            "customerNo": 0,
            "supplierNo": 50002
          },
          ...
        ]
      }
    }
  }
}

Filtering is very similar to an open-source GraphQL implementation for Postgres databases. You can read more about that here. This document can be used for finding more examples.

The filter for each table type (connection) has its own type. For instance, the filter type for the Associate table is called FilterExpression_Associate. You can see this if you explore the schema with GraphiQL, for instance:

Filter expression

FilterExpression_Associate type, in turn, has field for each column in the table. This fields are of a type called _FilterClause, such as Int32_FilterClause, LimitedString_FilterClause, etc. Two additional fields, _and and _or, of the same FilterExpression_Associate allow composing complex filter with the AND and OR logical operators.

Filter expression type

The filter clause type, in turn, has fields representing operators, such as _lt or “lower than” or _like for the text comparison like operator.

Filter clause type

The following table lists the available operators:

Operator Description
_eq equal to value
_gt greater than value
_gte greater than or equal to value
_in in list of values
_is_not_null is not null
_is_null is null
_is_off is off - available for bit field columns only
_is_on is on - available for bit field columns only
_lt less than value
_lte less than or equal to value
_like like value
_not_eq not equal value
_not_in not in list of values
_not_like not like value
_between between from and to (inclusive bounds)
_not_between not between from and to (exclusive bounds)

The filter can also be provided as an argument to the query. The filter value is an object of the filter expression type. This is shown in the following example:

Query

query GetGLAs(
  $companyNo: Int,
  $filter : FilterExpression_GeneralLedgerAccount)
{  
  useCompany(no: $companyNo)
  {
    generalLedgerAccount(filter: $filter)
    {
      totalCount
      items
      {
        accountNo
        name
      }
    }
  }
}
Variables

{
  "companyNo": 9112233,
  "filter": {"_and" :[
    {"accountNo" :{"_gte" : 6000}},
    {"accountNo" :{"_lt" : 7000}}]}
}

_in and _not_in operators

Most of the operators take a single value. The _in and _not_in operators, however, take an array of values, as show in the following example:

query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate (filter : {customerNo : {_in: [10001,10002,10003]}})
    {
      totalCount
      items
      {
        name
        associateNo
        customerNo
        supplierNo
      }
    }
  }
}

These two operators (_in and _not_in) are helpful for simplifying more complex filters. The following two tables show a filter using these two operators, and the equivalent expression without them.

With_in

query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate (filter : {
      customerNo : {_in: [10001,10002,10003]}})
    {
      totalCount
      items
      {
        name
        associateNo
        customerNo
        supplierNo
      }
    }
  }
}
Without_in

query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate (filter : {_or: [
      customerNo : {_eq: 10001},
      customerNo : {_eq: 10002},
      customerNo : {_eq: 10003}
    ]})
    {
      totalCount
      items
      {
        name
        associateNo
        customerNo
        supplierNo
      }
    }
  }
}
With_in

query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate (filter : {
      customerNo : {_not_in: [10001,10002,10003]}})
    {
      totalCount
      items
      {
        name
        associateNo
        customerNo
        supplierNo
      }
    }
  }
}
Without_in

query read($cid : Int!)
{
  useCompany(no: $cid)
  {
    associate (filter : {_and: [
      customerNo : {_not_eq: 10001},
      customerNo : {_not_eq: 10002},
      customerNo : {_not_eq: 10003}
    ]})
    {
      totalCount
      items
      {
        name
        associateNo
        customerNo
        supplierNo
      }
    }
  }
}

If the argument for the _in or _not_in operators is an empty array, the result will be an empty set.

An example is shown below, where we first query the Order table, exporting the value of the sellerOrBuyer column to the variable sellers. We then query the Associate table, filtering for the employees with the numbers in the sellers array. Because no order matches the filter, the result is an empty sellers array, which results in no data being returned.

Query

query find_selers($cid: Int!, $sellers: [Int!] = [])
{
  useCompany(no: $cid)
  {
    order(
      filter: {sellerOrBuyer: {_gt: 999}},
      first: 5)
    {
      items
      {
        sellerOrBuyer @export(as: "sellers")
      }
    }

    associate(
      filter: {employeeNo: {_in: $sellers}})
    {
      items
      {
        employeeNo
        name
      }
    }
  }
}
Result

{
  "data": {
    "useCompany": {
      "order": {
        "items": null
      },
      "associate": {
        "items": null
      }
    }
  }
}

If the _in or _not_in operators are used in an AND expression, and the argument is an empty array, the resulting filter will be an empty expression, and no data will be returned.

order(
  filter: {_and: [
     {changeDateTime : {_gte: {"2024-01-24T12:00:00"}}}
     {orderNo: {_in: []}}
  ]})
{
   items
   {
     orderNo
     dueDate
   }
}

This results in an empty filter. No data is returned when a filter is provided but it is empty. On the other hand, when no filter is provided, all the data is returned.

If the _in or _not_in operators are used in an OR expression and the argument is an empty array, the subexpression is removed from the filter expression, which may still produce a valid filter that can return data.

Filter

filter: {_or: [
     {changeDateTime :
        {_gte: {"2024-01-24T12:00:00"}}}
     {orderNo: {_in: []}}
  ]}
Equivalent

filter: {_or: [
     {changeDateTime :
        {_gte: {"2024-01-24T12:00:00"}}}
  ]}

_between / _not_between operators

These two operators require two values that define the lower and upper bounds of the range. The range is inclusive, meaning that the values that are equal to the lower or upper bound are included in the result.

query read_glas($cid : Int!, $pagesize : Int!)
{
  useCompany(no: $cid) 
  {
    generalLedgerAccount(
      first: $pagesize, 
      filter : {accountNo : {_between : {from: 1000, to: 2000}}})
    {
      totalCount
      items
      {
        accountNo
        name
      }
    }
  }  
}

These operators are support for fields of the following types:

  • numeric (Int, Long, Decimal, etc.)
  • Date
  • Time
  • DateTime

The following example shows an expression using both the _between operator (for an int field) and the _not_between operator (for a datetime field):

query read_glas($cid : Int!)
{
  useCompany(no: $cid)
  {
    generalLedgerAccount(filter: { _and : [
        {accountNo : {
            _not_between : { 
                from : 3000, 
                to   : 5000
            }
        }},
        {changedDateTime : { 
            _between : {
                from : "2024-05-01T12:00:00", 
                to   : "2024-05-30T23:59:59"
            }
        }}
      ]})
    {
      totalCount
      items
      {
        accountNo
        name
      }
    }
  }
}

Comparing with current date/time

The model define various fields for date and time. All the nodes that have the type Date_FilterClause, Time_FilterClause, or DateTime_FilterClause can be compared with the current date/time. These GraphQL types, have, in addition for fields defining operators, an additional field called _cmp whose type is ReferenceDateTime_FilterClause, which looks as follows:

ReferenceDateTime_FilterClause type

This type features nodes for all the comparison operators available for date and time fields. However, their type is ReferenceDateTime that has the following fields:

Name Type Description
day ReferenceDays The day this moment refers to. One of YESTERDAY, TODAY, or TOMORROW.
dayOffset Int The number of days to offset from the current date. For instance TODAY with an offset of one is TOMORROW, and TODAY with an offset of -1 is YESTERDAY.
time ReferenceTimePoints Can only have the value NOW.
minuteOffset Int The number of minutes to offset from the current time.

The following example shows a query that fetches all orders that where changed in the last hour:

query read_orders ($cid: Int) 
{
  useCompany(no: $cid)
  {
    order(filter : 
    {
      _and : [
        {
          changedDateAsDate : {
            _cmp : {
              _eq : {day : TODAY, dayOffset : 0}
            } 
          }
        },
        {
          changedTimeAsTime : {                
            _cmp : {                    
              _gte : {time : NOW, minuteOffset: -60}
            }
          }
        }
      ]            
    })
    {
      totalCount   
      items
      {
        orderNo
      }
    }
  }
}

This particular query can be simplified by using the createdDateTime field, as shown bellow:

query read_orders ($cid: Int) 
{
  useCompany(no: $cid)
  {
    order(filter : 
    {
      createdDateTime : {
        _cmp : {
          _gte : {
            day : TODAY, 
            time : NOW, 
            minuteOffset : -60}
        } 
      }            
    })
    {
       totalCount   
       items
       {
          orderNo
       }
    }
  }
}

Having expressions

The same operators described here for filters can be used for having clauses in queries that include aggregations.

To learn more about this, see Grouping: The having argument.

Last modified September 24, 2024