# Sorting, filtering and pagination



The LawVu API supports a subset of the [OData query language](https://www.odata.org/documentation/) to enable filtering, sorting, and pagination of collection resources. This guide explains how to use these query options effectively.

## Supported query options

The following OData query options are supported:

| Query Option | Description |
|  --- | --- |
| `$filter` | Filter the results based on field values |
| `$orderby` | Sort the results by one or more fields |
| `$top` | Limit the number of results returned |
| `$skip` | Skip a specified number of results (for pagination) |


## Pagination

Use `$top` and `$skip` to paginate through large result sets.

### $top

Limits the number of results returned in the response.

- **Default value:** 30
- **Maximum value:** 200
- **Minimum value:** 1



```http
# Limit results to 10 items
GET /v2/matters?$top=10
```

### $skip

Skips a specified number of results. Use this in combination with `$top` to implement pagination.

- **Default value:** 0
- **Minimum value:** 0



```http
# Skip the first 30 results and return the next 10
GET /v2/matters?$skip=30&$top=10
```

### Pagination example

To retrieve results in pages of 10:


```http
# Retrieve the first 10 results
GET /v2/matters?$top=10

# Skip the first 10 results and retrieve the next 10
GET /v2/matters?$top=10&$skip=10

# Page 3: Skip the first 20 results and retrieve the next 10
GET /v2/matters?$top=10&$skip=20
```

## Sorting with $orderby

Sort results by one or more fields using the `$orderby` query option. Each field can be sorted in ascending (`asc`) or descending (`desc`) order.

### Syntax


```
$orderby=field [asc|desc]
$orderby=field1 [asc|desc],field2 [asc|desc]
```

If no direction is specified, ascending order is used by default.

### Examples

Sort by name in ascending order:


```http
# Sort matters alphabetically by name (A-Z)
GET /v2/matters?$orderby=Name asc
```

Sort by name in descending order:


```http
# Sort matters reverse-alphabetically by name (Z-A)
GET /v2/matters?$orderby=Name desc
```

Sort by multiple fields:


```http
# Sort by name ascending, then by status descending
GET /v2/matters?$orderby=Name asc,Status desc
```

> **Note:** Not all fields support sorting. Refer to the [Supported resources](#supported-resources) section below for a list of sortable fields for each resource type.


## Filtering with $filter

The `$filter` query option allows you to filter results based on field values using comparison operators, logical operators, and collection operators.

### Comparison operators

| Operator | Description | Example |
|  --- | --- | --- |
| `eq` | Equal | `Name eq 'Project Alpha'` |
| `eq null` | Equal null | `Name eq null` |
| `ne` | Not equal | `Status ne 'Active'` |
| `ne null` | Not equal null | `Name ne null` |
| `gt` | Greater than | `Created/DateUtc gt '2024-01-01'` |
| `ge` | Greater than or equal | `Created/DateUtc ge '2024-01-01T12:30:00Z'` |
| `lt` | Less than | `Id lt 1234` |
| `le` | Less than or equal | `Fields/slider le 5` |
| `contains` | Contains | `Contains(name, 'Alpha')` |


### Filter examples


```http
# Filter by exact match
GET /v2/matters?$filter=Status eq 'Active'

# Filter by greater than
GET /v2/matters?$filter=Created/DateUtc gt '2024-01-01'

# Filter where a field is null
GET /v2/matters?$filter=ExternalId eq null

# Filter where a field is not null
GET /v2/matters?$filter=ExternalId ne null

# Filter by contains
GET /v2/matters?$filter=contains(Name, 'Alpha')
```

> **Note:** Not all fields support all comparisons. Refer to the [Supported resources](#supported-resources) section below for a list of sortable fields for each resource type.


### Logical operators

Combine multiple conditions using logical operators:

| Operator | Description | Example |
|  --- | --- | --- |
| `and` | Logical AND | `Status eq 'Active' and Restricted eq false` |
| `or` | Logical OR | `Status eq 'Active' or Status eq 'Draft'` |


### Collection operators

The `any` operator iterates over a collection and returns true if at least one item matches the specified condition.


```
collectionProperty/any(property:property <operator> <value>)
collectionProperty/any(property:property/subProperty <operator> <value>)
```

Where:

- `collectionProperty` is the path to the collection
- `property` is a lambda variable representing each item in the collection
- `subProperty` is a reference to a sub property on the collection entity when the entity is another entity
- `operator` is a comparison operator
- `value` is the value to apply the operator to


### Filter examples


```http
# Filter matters where the lookup field contains 'option_a'
GET /v2/matters?$filter=Fields/lookup/any(lookup:lookup/value eq 'option_a')

# Filter matters where the lookup field contains either 'option_a' or 'option_b'
GET /v2/matters?$filter=Fields/lookup/any(lookup:lookup/value eq 'option_a' or lookup/value eq 'option_b')
```

> **Note:** The `any` operator only supports `or` logic within the lambda expression. Using `and` logic within an `any` expression is not supported.


> **Note:** Not all collection properties support the `any` operator. Refer to the specific endpoint documentation and the [Supported resources](#supported-resources) section in this guide for details on which collections can be filtered.


### Grouping with parentheses

Use parentheses to group conditions and control evaluation order:


```http
# Filter for non-restricted matters that are either Active or Draft
GET /v2/matters?$filter=(Status eq 'Active' or Status eq 'Draft') and Restricted eq false
```

## Nested properties

Some fields represent nested objects. Use the forward slash (`/`) to access nested properties in filter and orderby expressions.

**Example:**


```http
# Filter matters by the owner's user ID
GET /v2/matters?$filter=Owner/Id eq '12345678-1234-1234-1234-123456789abc'

# Filter matters by the ID of the user who created them
GET /v2/matters?$filter=Created/User/Id eq '12345678-1234-1234-1234-123456789abc'
```

## Error handling

When an invalid OData query is submitted, the API returns a `400 Bad Request` response with details about the error.

> Note: Refer to the [Errors](/docs/errors) documentation for more information.


### Common error scenarios

**Unsupported field:**


```json
{
  "type": "https://api-docs.lawvu.com/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "OData filtering is not supported for 'unsupportedField'"
}
```

**Unsupported operator:**


```json
{
  "type": "https://api-docs.lawvu.com/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "Operation 'gt' is not supported for 'Name'"
}
```

**Invalid $top value:**


```json
{
  "type": "https://api-docs.lawvu.com/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "OData $top value must be less than 50"
}
```

**Invalid query syntax:**


```json
{
  "type": "https://api-docs.lawvu.com/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The OData query is invalid. Please check the syntax and try again"
}
```

## Supported resources

The following sections describe the resources and the properties on the resource which are supported for OData filtering and ordering.

### Field Options

All field types support the following base filtering:

| Property | Filter Operations | Functions | Sortable |
|  --- | --- | --- | --- |
| Label | eq, ne | contains() | ✓ |
| Value | eq, ne | - |  |


The following types additionally support filtering on [properties](/docs/guides/fields#property-bags):

| LawVu Application Type | Property | Filter Operations | Functions |
|  --- | --- | --- | --- |
| Department | Properties.parentId | eq, eq null, ne, ne null | - |


### Contracts

| Property | Filter Operations | Functions | Sortable |
|  --- | --- | --- | --- |
| Id | eq, ne | - | ✓ |
| Name | eq, ne | contains() | ✓ |
| Type/Id | eq, ne | - | - |
| Owner/Id | eq, ne | - | ✓ |
| ExternalId | eq, ne | contains() | - |
| Fields | Refer to [fields](#fields-on-matters-and-contracts) | - | - |
| Restricted | eq, ne | - | - |
| Status | eq, ne | - | - |
| TeamAssigned/Id | eq, ne | - | ✓ |
| Created/User/Id | eq, ne | - | - |


### Matters

| Property | Filter Operations | Functions | Sortable |
|  --- | --- | --- | --- |
| Id | eq, ne | - | ✓ |
| Name | eq, ne | contains() | ✓ |
| DisplayId | eq, ne | contains() | - |
| Type/Id | eq, ne | - | - |
| Owner/Id | eq, ne | - | ✓ |
| Manager/User/Id | eq, eq null, ne, ne null | - | ✓ |
| Manager/IntakeQueue/OrganizationId | eq, eq null, ne, ne null | - | - |
| ExternalId | eq, ne | contains() | - |
| Fields | Refer to [fields](#fields-on-matters-and-contracts) | - | - |
| Restricted | eq, ne | - | - |
| Status | eq, ne | - | - |
| TeamAssigned/Id | eq, ne | - | ✓ |
| Created/User/Id | eq, ne | - | - |


### Fields on Matters and Contracts

Refer to the below table for supported operations when filtering or sorting by matters/contract field values.

| Field Type | Filter Operations | Functions | Sortable |
|  --- | --- | --- | --- |
| Text | eq, eq null, ne, ne null | contains() | - |
| Decimal | eq, eq null, ne, ge, le | - | - |
| Decimal (Slider) | eq, ne, ge, le | - | - |
| Boolean | eq | - | - |
| Date | ge, lt, le | - | - |
| Option/Value | eq, eq null, ne, ne null | - | - |
| MultiOption/Value | eq | - | - |
| User/Id | eq, eq null, ne, ne null | - | - |


> Note: Refer to [fields documentation](/docs/guides/fields#how-the-field-types-in-the-api-map-to-the-lawvu-application) for information on how the field type maps to the LawVu application.