Skip to main content

Sorting Syntax

CargoX supports flexible sorting for list queries. You can sort by single or multiple fields, in ascending or descending order, and even sort by properties within filtered collections.

Basic Syntax

To sort by a field, pass the field name to the orderBy parameter:

query {
orders(organizationId: 1, orderBy: "orderNumber") {
items {
orderNumber
created
}
}
}

Descending Order

To sort in descending order, prefix the field name with a minus sign (-):

query {
orders(organizationId: 1, orderBy: "-created") {
items {
orderNumber
created
}
}
}

Multiple Sort Fields

To sort by multiple fields, separate them with commas. The first field is the primary sort, the second is secondary, and so on:

query {
orders(organizationId: 1, orderBy: "orderType,orderNumber") {
items {
orderType
orderNumber
}
}
}

You can mix ascending and descending:

query {
orders(organizationId: 1, orderBy: "-created,orderNumber") {
items {
created
orderNumber
}
}
}

Sorting by Nested Properties

To sort by a property of a related entity, use dot notation:

query {
states(organizationId: 1, orderBy: "country.name") {
items {
stateCode
name
country {
name
}
}
}
}

Examples

orderByDescription
country.nameSort by the country's name
-country.nameSort by the country's name descending
billToContact.nameSort by the bill-to contact's name
orderStatus.nameSort by the order status name

Sorting by Collection Properties

When sorting by a property within a collection (one-to-many relationship), the system uses the first item in the collection:

query {
commodities(organizationId: 1, orderBy: "orderCommodities.order.orderNumber") {
items {
description
}
}
}

Filtered Collection Sorting

For more precise control when sorting by collection properties, you can filter the collection before selecting the sort value. This is useful when entities have multiple related items of different types.

Syntax

collection[filterPath:filterValue].property

Where:

  • collection - The name of the collection property
  • filterPath - The path to the property to filter on (can include dots for nested properties)
  • filterValue - The value to filter by
  • property - The property to sort by after filtering

Examples

Sorting by a Specific Entity Type

Sort orders by the name of their "UltimateConsignee" entity:

query {
orders(organizationId: 1, orderBy: "orderEntities[entityType:UltimateConsignee].contact.name") {
items {
orderNumber
}
}
}

Sorting by a Specific Order Type

Sort commodities by the bill-to contact name of their "ParcelShipment" orders:

query {
commodities(organizationId: 1, orderBy: "orderCommodities[order.orderType:ParcelShipment].order.billToContact.name") {
items {
description
}
}
}

Sorting Countries by Specific State

Sort countries by the name of their state with code "CA":

query {
countries(organizationId: 1, orderBy: "states[stateCode:CA].name") {
items {
countryCode
name
}
}
}

Supported Filter Types

The filter value can be:

TypeExampleDescription
String[type:ParcelShipment]Exact string match
Enum[entityType:UltimateConsignee]Enum value (case-insensitive)
Integer[priority:1]Integer value
Boolean[isActive:true]Boolean value
GUID[id:550e8400-e29b-41d4-a716-446655440000]GUID value

Nested Filter Paths

The filter path can include nested properties using dot notation:

query {
commodities(organizationId: 1, orderBy: "orderCommodities[order.orderType:ParcelShipment].order.billToContact.name") {
items {
description
}
}
}

Descending Order with Filtered Collections

Prefix with minus sign for descending order:

query {
orders(organizationId: 1, orderBy: "-orderEntities[entityType:Carrier].contact.name") {
items {
orderNumber
}
}
}

Multiple Sort Fields with Filtered Collections

Combine filtered collection sorting with other sort fields:

query {
commodities(organizationId: 1, orderBy: "description,orderCommodities[order.orderType:ParcelShipment].order.billToContact.name") {
items {
description
}
}
}

Handling No Matches

When no items in the collection match the filter, the sort value is treated as null. In ascending order, nulls appear first; in descending order, nulls appear last.

Sorting by Last Tracking Event

Orders and commodities expose a synthetic lastTrackingEvent sort path that ranks the entire result set by the "winner" tracking event directly at the SQL level — before rows are returned. This is distinct from the field-level getLastTrackingEvent resolver, which resolves one event per row after the list is fetched.

Basic Usage

Sort orders by their most recent tracking event date (descending — newest first):

query {
orders(organizationId: 1, orderBy: "-lastTrackingEvent.eventDate") {
items {
orderId
orderNumber
}
}
}

Sort ascending (oldest tracking event first):

query {
orders(organizationId: 1, orderBy: "lastTrackingEvent.eventDate") {
items {
orderId
orderNumber
}
}
}

Scoping by Event Definition

Use the [filterPath:filterValue] bracket syntax to restrict the sort to a specific event type:

query {
orders(
organizationId: 1
orderBy: "-lastTrackingEvent[eventDefinition.eventName:Departed].eventDate"
) {
items {
orderId
orderNumber
}
}
}

This sorts orders by the date of their most recent Departed event, ignoring all other event types.

Supported Sub-Paths

orderBy expressionSorts by
lastTrackingEvent.eventDateCOALESCE(EventDate, Created) of the winning event
lastTrackingEvent.trackingEventIdTrackingEventId of the winning event
lastTrackingEvent[eventDefinition.eventName:X].eventDateCOALESCE(EventDate, Created) filtered to event type X
note

When the sub-path is .eventDate, the emitted SQL selector is COALESCE(winner.EventDate, winner.Created) — matching the canonical winner-picking rule — so the outer ORDER BY never sees null for events that only have a Created timestamp.

Winner-Picking Rule

The "winning" event is selected using the same canonical ordering used by the getLastTrackingEvent DataLoader:

  1. COALESCE(EventDate, Created) DESC — most recent effective date wins
  2. TrackingEventId DESC — tie-breaker when two events share the same effective date

This ensures that SQL-level list sorting and per-row field resolution agree on which event is "the last".

Combining with Other Sort Fields

query {
orders(
organizationId: 1
orderBy: "-lastTrackingEvent[eventDefinition.eventName:Departed].eventDate,orderNumber"
) {
items {
orderId
orderNumber
}
}
}

Handling Orders with No Matching Event

When no tracking event matches (or the order has no events at all), the sort value is null. Null handling follows standard SQL behaviour: nulls sort last in descending order and first in ascending order.

Sorting by Custom Values (JSON Fields)

To sort by a custom value stored in a JSON field, use dot notation:

query {
orders(organizationId: 1, orderBy: "customValues.priority") {
items {
orderNumber
customValues
}
}
}

Case Sensitivity

Field names in sort expressions are case-insensitive:

# These are equivalent:
orderBy: "orderNumber"
orderBy: "OrderNumber"
orderBy: "ORDERNUMBER"

Complete Examples

Example 1: Sort Orders by Carrier Name

query {
orders(organizationId: 1, orderBy: "orderEntities[entityType:Carrier].contact.name") {
items {
orderNumber
orderEntities {
entityType
contact {
name
}
}
}
}
}

Example 2: Sort Commodities by Customer Name (from ParcelShipment orders)

query {
commodities(organizationId: 1, orderBy: "orderCommodities[order.orderType:ParcelShipment].order.billToContact.name") {
items {
commodityId
description
}
}
}

Example 3: Multiple Sort with Filtered Collection

query {
orders(
organizationId: 1
orderBy: "orderType,-orderEntities[entityType:UltimateConsignee].contact.name,orderNumber"
) {
items {
orderType
orderNumber
}
}
}

SQL Translation

The filtered collection sorting translates to efficient SQL subqueries. For example:

-- orderBy: "orderEntities[entityType:UltimateConsignee].contact.name"
ORDER BY (
SELECT c.name
FROM order_entities oe
JOIN contacts c ON oe.contact_id = c.contact_id
WHERE oe.order_id = o.order_id
AND oe.entity_type = 'UltimateConsignee'
LIMIT 1
)

This ensures optimal database performance even with complex sorting requirements.