Skip to content

Filtering & Sorting

Apilane provides a powerful JSON-based filtering and sorting system for querying data. The filter parameter is accepted by Data/Get, Files/Get, Stats/Aggregate and Stats/Distinct. The sort, properties and getTotal parameters are accepted by Data/Get and Files/Get; the GetByID endpoints take properties only. Stats/Aggregate has its own pageIndex, pageSize and orderDirection (ASC or DESC, default DESC), which orders the groups by their aggregated value.

Filtering

The filter query parameter accepts a JSON object that defines one or more conditions.

Simple Filter

A single condition compares a property to a value:

{
  "Property": "Email",
  "Operator": "equal",
  "Value": "[email protected]"
}

Compound Filter

Combine multiple conditions with AND or OR logic:

{
  "Logic": "AND",
  "Filters": [
    { "Property": "Age", "Operator": "greaterorequal", "Value": 18 },
    { "Property": "IsActive", "Operator": "equal", "Value": true }
  ]
}

Nested Filters

Filters can be nested to create complex expressions:

{
  "Logic": "AND",
  "Filters": [
    { "Property": "Country", "Operator": "equal", "Value": "US" },
    {
      "Logic": "OR",
      "Filters": [
        { "Property": "Role", "Operator": "equal", "Value": "admin" },
        { "Property": "Role", "Operator": "equal", "Value": "manager" }
      ]
    }
  ]
}

This translates to: Country = 'US' AND (Role = 'admin' OR Role = 'manager')

Filter Operators

Operator Description Applicable types
equal Equal (text ignores case, see below) All
notequal Not equal All
greater Greater than Number, Date, String
greaterorequal Greater than or equal Number, Date, String
less Less than Number, Date, String
lessorequal Less than or equal Number, Date, String
startswith Starts with String
endswith Ends with String
contains Contains the text. On a number: is one of the listed numbers (see below) String, Number
notcontains Does not contain the text. On a number: is none of the listed numbers String, Number

Write the operator in full, as in the table. Letter case does not matter (Equal works), but short forms such as eq, = or >= are refused with INVALID_FILTER_PARAMETER. A Boolean property accepts only equal and notequal.

String values are matched literally: %, _, [ and \ are ordinary characters, not wildcards or escapes, so send values unescaped. For example, equal cust_1 does not match custA1, and contains % matches only values that contain a percent sign. Spaces at the start and end of a value are removed before it is compared.

equal, notequal, startswith, endswith, contains and notcontains ignore case (on SQLite only for A–Z). On SQL Server and MySQL, case, accent and similar rules follow the database collation, which is case-insensitive by default. greater, greaterorequal, less and lessorequal on text use the ordering of the database, which on SQLite is binary: capitals sort before lower-case letters.

Values

How a value is written depends on the type of the property:

Property type Value
String Text. Spaces at the start and end are removed.
Number A number such as 3 or 3.5. With contains and notcontains, a comma-separated list of numbers (see below).
Boolean true or false (1, 0, on and off also work). Only equal and notequal.
Date Unix time in seconds (10 digits) or milliseconds (13 digits), or yyyy-MM-dd, yyyy-MM-dd HH:mm, yyyy-MM-dd HH:mm:ss or yyyy-MM-dd HH:mm:ss.fff, all in UTC. Text with a T or a Z (ISO 8601) is refused.
any null: equal finds the records where the property is empty, notequal the ones where it is not. No other operator accepts null.

A filter on a property that does not exist, and a filter or sort on a property the caller has no read access to, is refused (INVALID_FILTER_PARAMETER, INVALID_SORT_PARAMETER). A group inside another group must not have an empty Filters list: the query fails.

Encrypted properties

The value of an encrypted property is stored encrypted and a filter compares it as stored, so a filter with the plain value finds nothing, and sorting orders the encrypted text. Do not filter or sort by an encrypted property.

Matching a set of values (IN)

Apilane has no dedicated IN operator. On a numeric property, contains and notcontains compile to SQL IN (...) / NOT IN (...) when the value is a comma-separated list of numbers. This is the established Apilane idiom for "match any of these IDs":

{ "Property": "ID", "Operator": "contains", "Value": "3,7,42" }

translates to ID IN (3, 7, 42).

Numeric only

On a string property, contains means substring LIKE — not IN. The set-membership behaviour applies only to numeric properties.

With the SDK, build the value with string.Join(",", ids):

// .NET — "ID IN (...)"
var filter = new FilterItem("ID", FilterOperator.contains, string.Join(",", ids));
// JavaScript — "ID IN (...)"
const filter = FilterItem.condition('ID', FilterOperator.contains, ids.join(','));

Filter Logic

Logic Description
AND All conditions must be true
OR At least one condition must be true

URL Encoding

Since filters are passed as a query parameter, the JSON must be URL-encoded:

GET https://my.api.server/api/Data/Get?entity=Products&filter=%7B%22Property%22%3A%22Price%22%2C%22Operator%22%3A%22less%22%2C%22Value%22%3A100%7D
x-application-token: {appToken}

Tip

Use the .NET SDK to build filters with a type-safe builder pattern instead of constructing JSON manually.

Sorting

The sort query parameter accepts a JSON object that specifies the property and direction:

{
  "Property": "Created",
  "Direction": "DESC"
}

Sort Directions

Direction Description
ASC Ascending (smallest first)
DESC Descending (largest first)

The direction is not case sensitive, and anything other than DESC sorts ascending.

Several properties

Send an array to sort by several properties; the next one decides between records that are equal in the ones before it:

[
  { "Property": "Created", "Direction": "DESC" },
  { "Property": "Title", "Direction": "ASC" }
]

A property that does not exist is ignored; one the caller cannot read is refused (INVALID_SORT_PARAMETER). Without sort, Data/Get returns the records in the default sorting of the entity (the Sorting tab of the entity in the Portal), and without that by ID, ascending. The .NET SDK WithSort takes one property.

Sorting Example

Get products sorted by price ascending:

GET https://my.api.server/api/Data/Get?entity=Products&sort={"Property":"Price","Direction":"ASC"}
x-application-token: {appToken}

Paging

Data/Get, Files/Get and Stats/Aggregate support paging with two parameters (Data/GetHistoryByID has them too, with a default page size of 10):

Parameter Default Range Description
pageIndex 1 1+ The page number to retrieve. A smaller value is read as 1
pageSize 20 1-1000 Number of records per page. A value below 1 (0 included) or above 1000 is read as 1000

A single call never returns more than 1000 records. To read more, request the next pageIndex until a page comes back with fewer records than pageSize.

Getting Total Count

Set getTotal=true to include the total record count in the response. This is useful for building pagination UIs.

Without total:

{
  "Data": [ { "ID": 1, "Name": "Widget" }, ... ]
}

With total:

{
  "Data": [ { "ID": 1, "Name": "Widget" }, ... ],
  "Total": 142
}

Performance

Getting the total count adds an extra database query. Only request it when needed (e.g., for the first page load or when building a pager).

Property Selection

Use the properties parameter to fetch only specific properties. This reduces bandwidth and improves performance.

GET https://my.api.server/api/Data/Get?entity=Products&properties=ID,Name,Price
x-application-token: {appToken}

When omitted, all properties accessible to the user are returned.

SDK Filter Builder

The .NET SDK provides a type-safe way to build filters:

using Apilane.Net.Models.Data;
using Apilane.Net.Models.Enums;

// Simple filter
var filter = new FilterItem("Price", FilterOperator.less, 100);

// Compound filter
var filter = new FilterItem(FilterLogic.AND, new List<FilterItem>
{
    new FilterItem("Age", FilterOperator.greaterorequal, 18),
    new FilterItem("IsActive", FilterOperator.equal, true)
});

// Use in a request
var response = await _apilaneService.GetDataAsync<Product>(
    DataGetListRequest.New("Products")
        .WithAuthToken(authToken)
        .WithFilter(filter)
        .WithSort(new SortItem { Property = "Price", Direction = "ASC" })
        .WithPageSize(50)
        .WithProperties("ID", "Name", "Price"));