# Search Query Language

Several PartsSource GET search endpoints accept a structured query via the `q` query parameter. This language supports exact matching, substring matching, numeric and date comparisons, negation, and boolean logic — giving you precise control over which records are returned.

{% hint style="info" %}
Each API defines its own searchable fields. See the field reference for your API:

* [Customer API — Searchable Fields](https://github.com/PartsSourceInc/gitbook-documentation/blob/main/customer-api/searchable-fields.md)
  {% endhint %}

***

## Quick Start

Search for manufacturers by name:

```bash
curl -G "https://api.partssource.com/customer/api/manufacturers/search" \
  -H "Authorization: Bearer <token>" \
  --data-urlencode 'q=name~"philips"' \
  -d "limit=20"
```

Response:

```json
{
  "data": {
    "items": [
      { "manufacturerId": 26799, "name": "Philips Healthcare" }
    ],
    "pagination": {
      "total": 1,
      "limit": 20,
      "offset": 0,
      "hasMore": false
    }
  }
}
```

***

## Query Syntax

A query consists of one or more **clauses** joined by `AND`. Each clause targets a single field.

```
<clause> [AND <clause>]*
```

A **clause** has the form:

```
[-]<field><operator><value>
```

| Component      | Description                                                         |
| -------------- | ------------------------------------------------------------------- |
| `-` (optional) | Negation prefix. Excludes matching records.                         |
| `field`        | A searchable field name defined for the endpoint. Case-insensitive. |
| `operator`     | One of `:`, `~`, `>`, `<`, `>=`, `<=`.                              |
| `value`        | A quoted string (`"..."`) or unquoted numeric/keyword value.        |

***

## Operators

| Operator | Name                  | Description                                                               | Applies to                       |
| -------- | --------------------- | ------------------------------------------------------------------------- | -------------------------------- |
| `:`      | Exact match           | Case-insensitive equality. For String fields, matches the complete value. | Token, String, Numeric, DateTime |
| `~`      | Substring             | Case-insensitive substring match. Minimum 3 characters required.          | String only                      |
| `>`      | Greater than          | Strictly greater than the given value.                                    | Numeric, DateTime                |
| `<`      | Less than             | Strictly less than the given value.                                       | Numeric, DateTime                |
| `>=`     | Greater than or equal | Greater than or equal to the given value.                                 | Numeric, DateTime                |
| `<=`     | Less than or equal    | Less than or equal to the given value.                                    | Numeric, DateTime                |

### Operator Compatibility by Field Type

| Field Type | `:` | `~` | `>` `<` `>=` `<=` |
| ---------- | --- | --- | ----------------- |
| Token      | Yes | No  | No                |
| String     | Yes | Yes | No                |
| Numeric    | Yes | No  | Yes               |
| DateTime   | Yes | No  | Yes               |

Using an operator incompatible with a field's type returns a `400 Bad Request` error.

***

## Field Types

Each searchable field has one of four types. The type determines which operators are valid and how values are interpreted.

### Token

Exact-match identifiers, statuses, and enum values. Compared case-insensitively. Not searchable by substring.

```
status:"Pending"
active:"Y"
```

### String

Free-text fields like names, descriptions, and email addresses. Support both exact match (`:`) and substring match (`~`). All comparisons are case-insensitive.

```
email:"john@example.com"       # exact match
firstName~"joh"                 # substring match (min 3 chars)
companyName:"Acme Corp"         # exact match
```

### Numeric

Integer or decimal fields like IDs and amounts. Support exact match and range comparisons. Values must not be quoted.

```
companyId:500                   # exact
orderId>10000                   # range
```

### DateTime

Date and timestamp fields. Support exact match and range comparisons. Values use ISO 8601 format and must be quoted.

```
createdDate:"2026-01-15"                    # exact date
createdDate>="2026-01-01"                   # on or after
createdDate<"2026-02-01"                    # before
```

***

## Boolean Logic

Multiple clauses must be joined with the `AND` keyword.

```
status:"Pending" AND companyId:500
```

### Restrictions

* **Maximum 10 clauses** per query.
* Only `AND` logic is supported.
* **No parentheses** for grouping or operator precedence.

***

## Negation

Prefix a clause with `-` to exclude matching records.

```
-status:"Completed"                          # everything except Completed
-email~"test"                                # exclude test emails
```

Negation inverts the clause. When combined with `AND`, all negated conditions are excluded.

***

## Null Checks

Use the `null` keyword (case-insensitive) as a value with the exact-match operator to test for field presence.

```
email:null                                   # field is null / missing
-email:null                                  # field is not null / exists
```

***

## Value Quoting Rules

| Value type      | Quoting                              | Examples                         |
| --------------- | ------------------------------------ | -------------------------------- |
| String values   | **Required** — wrap in double quotes | `email:"john@example.com"`       |
| Numeric values  | **Not required** — bare numbers      | `companyId:500`                  |
| DateTime values | **Required** — wrap in double quotes | `createdDate:"2026-01-15"`       |
| Null keyword    | **Not quoted**                       | `email:null`                     |
| Embedded quotes | Escape with backslash                | `description:"called \"Title\""` |

***

## Pagination and Sorting

GET search endpoints use **query parameter pagination** rather than a request body.

```
GET /{resource}/search?q=<query>&limit=<int>&offset=<int>&sortBy=<field>&sortDir=<asc|desc>
```

| Parameter | Required | Default          | Constraints                                                   |
| --------- | -------- | ---------------- | ------------------------------------------------------------- |
| `q`       | No       | —                | Valid search query string. When omitted, returns all results. |
| `limit`   | No       | `50`             | 1–100                                                         |
| `offset`  | No       | `0`              | >= 0                                                          |
| `sortBy`  | No       | Resource default | Must be a valid sort field for the resource                   |
| `sortDir` | No       | `Asc`            | `Asc` or `Desc`                                               |

### Response Format

```json
{
  "data": {
    "items": [ ... ],
    "pagination": {
      "total": 142,
      "limit": 50,
      "offset": 0,
      "hasMore": true
    }
  }
}
```

{% hint style="info" %}
For general pagination patterns (navigating pages, performance tips, code examples), see [Pagination](https://docs.partssource.com/documentation/core-concepts/pagination).
{% endhint %}

***

## Error Responses

Parse errors are returned as `400 Bad Request` with an RFC 7807 problem details body.

### Unknown field

```json
{
  "type": "https://tools.ietf.org/html/rfc7807",
  "title": "Invalid search query",
  "status": 400,
  "detail": "Unknown field 'foo'. Valid fields: email, firstName, lastName, customerId, userName, companyName"
}
```

### Invalid operator for field type

```json
{
  "type": "https://tools.ietf.org/html/rfc7807",
  "title": "Invalid search query",
  "status": 400,
  "detail": "Operator '~' is not supported for Numeric field 'companyId'. Use ':' for exact match or '>', '<', '>=', '<=' for range."
}
```

### Substring too short

```json
{
  "type": "https://tools.ietf.org/html/rfc7807",
  "title": "Invalid search query",
  "status": 400,
  "detail": "Substring match '~' requires at least 3 characters. Got: 'ab'"
}
```

### Too many clauses

```json
{
  "type": "https://tools.ietf.org/html/rfc7807",
  "title": "Invalid search query",
  "status": 400,
  "detail": "Search query exceeds the maximum of 10 clauses."
}
```

***

## Examples

### Find manufacturer by exact name

```
GET /manufacturers/search?q=name:"Siemens Healthineers"
```

### Find manufacturers by name substring

```
GET /manufacturers/search?q=name~"philips"
```

### Paginate through manufacturer results with sorting

```
GET /manufacturers/search?q=name~"health"&limit=25&offset=0&sortBy=name&sortDir=Asc
```

### Find users by search query

```
GET /users/search?q=email~"@hospital.org"&limit=20
```
