# Querying records

Source: https://developers.swell.is/backend-api/querying

Some models have an optional property called `secondary_field` that determines whether a model can be referenced with a value in the URL other than `primary_field` (which is always `id`). All models have a `primary_field` (`id`) while only the following models feature a `secondary_field` :

| Model | secondary_field value |
| --- | --- |
| Carts model | number |
| Categories model | slug |
| Customers model | email |
| Gift Card model | code |
| Invoices model | number |
| Orders model | number |
| Pages model | slug |
| Payments model | number |
| Products model | slug |
| Purchase Links model | name |
| Returns model | number |
| Shipments model | number |

## Parameters

When making an API request, a configuration object with query parameters is passed to customize the request.

### Fields

- `where` (object): An object containing criteria to filter results by.
- `fields` (string): Fields to include in the result, as a comma-separated string or an array. Supports dot-notation for nested fields.
- `sort` (string): A string expression used to sort results.
- `limit` (int): A number indicating the max number of results to return up to 1000. Default: `15`.
- `limit_count` (int): Limits how many records are examined when counting a large collection. Values below 1000 are ignored.
- `page` (int): A number indicating which page of results to return.
- `skip` (int): Number of records to skip, overriding the offset normally computed from `page` and `limit`.
- `window` (int): Number of entries in the `pages` map of a paginated result. Defaults to the total page count.
- `search` (string): A string to search and filter results by.
- `expand` (string): A string or array of fields to expand in the result.
- `include` (object): An object with additional queries to include as fields in the result.
- `group` (object): Simplified aggregation with accumulator and date operators. See the Aggregation article.
- `aggregate` (array of object): Complete MongoDB aggregation pipeline. See the Aggregation article.


## Filtering

Use the `where` parameter to filter results by field values. Swell offers native support for many [MongoDB Query Operators](https://www.mongodb.com/docs/v4.4/reference/operator/query/), and you can combine multiple fields and operators together.

Top-level query parameters that aren't reserved are treated as filters automatically—`?active=true` is equivalent to `where[active]=true`.

#### Comparison and evaluation operators

These operators are used to filter results based on the values of certain fields, with matching based on existence, data type, regex evaluation, equality, and greater/less than comparisons.

| Name | Description |
| --- | --- |
| $eq | Matches values that are equal to a specified value. |
| $ne | Matches values that are not equal to a specified value. |
| $gt | Matches values that are greater than a specified value. |
| $gte | Matches values that are greater than or equal to a specified value. |
| $lt | Matches values that are less than a specified value. |
| $lte | Matches values that are less than or equal to a specified value. |
| $in | Matches any of the values specified in an array. |
| $nin | Matches none of the values specified in an array. |
| $regex | Selects records where values match a specified regular expression. |
| $type | Matches records if a field is of a specified type. |
| $exists | Matches records that have a specified field defined. |

**Comparison and evaluation operator examples**

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

// Find products on sale
await swell.get('/products', {
	where: { 
		sale: true
	}
});

// Find products with price above $10
await swell.get('/products', {
	where: { 
		price: { $gt: 10 }
	}
});

// Find products with price of $10 or above
await swell.get('/products', {
	where: { 
		price: { $gte: 10 }
	}
});

// Find products with price below $50
await swell.get('/products', {
	where: { 
		price: { $lt: 50 }
	}
});

// Find products with price of $50 or less
await swell.get('/products', {
	where: { 
		price: { $lte: 50 }
	}
});

// Find products with price between $10 and $50
await swell.get('/products', {
	where: { 
		price: { $gte: 10, $lte: 20 }
	}
});

// Find products with a material attribute of 'Silver', 'Gold', or 'Titanium'
await swell.get('/products', {
	where: { 
    'attributes.material': { $in: ['Silver', 'Gold', 'Titanium'] }
	}
});

// Find products without a material attribute of 'Polyester' or 'Nylon'
await swell.get('/products', {
	where: { 
		'attributes.material': { $nin: ['Polyester', 'Nylon'] }
	}
});

// Find products that contain the word 'chair' in the name, ignoring case
await swell.get('/products', {
	where: {
		name: { $regex: 'chair', $options: 'i' }
	}
});

// Find products with a subscription purchase option
await swell.get('/products', {
  where: {
    'purchase_options.subscription': { $exists: true }
  }
});

// Find products with a SKU string defined
await swell.get('/products', {
  where: {
    sku: { $type: 'string' }
  }
});
```

#### Logical operators

Logical operators are used to define multiple conditions in a query with logical evaluations. By default, Swell parses multiple attributes in a where object with the $and operator, so you don’t need to explicitly use this unless your query also requires another type of logical operator.

| Name | Description |
| --- | --- |
| $and | Joins query clauses with a logical AND operation. |
| $or | Joins query clauses with a logical OR operation. |
| $nor | Joins query clauses with a logical NOR returns all records that fail to match both clauses. |
| $not | Inverts the effect of an operator expression on a single field. |

**Logical operator examples**

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

// Find orders created between July 1st, 2022 and June 30th, 2023
await swell.get('/orders', {
	where: {
		$and: [
			{ date_created: { $gt: '2022-07-01T00:00:00Z' }},
		  { date_created: { $lt: '2023-06-30T00:00:00Z' }}
		]
	}
});

// This shorthand syntax also works
await swell.get('/orders', {
	where: {
		date_created: { $gt: '2022-07-01T00:00:00Z', $lt: '2023-06-30T00:00:00Z' }
	}
});

// Find active products with a stock level below 10
await swell.get('/products', {
  where: {
		$and: [
	    { stock_level: { $lt: 10 }},
	    { active: true},
		]
  },
});

// This shorthand syntax also works
await swell.get('/products', {
  where: {
    stock_level: { $lt: 10 },
    active: true,
  },
});

// Find products with a material attribute of 'Silver' and a price under $100.
await swell.get('/products', {
	where: {
		$and: [
			{ 'attributes.material': 'Silver' },
			{ price: { $lt: 100 }}
		] 
	}
});

// Find products that are on sale or have a price under $50.
await swell.get('/products', {
	where: {
		$or: [
			{ sale: true }, 
			{ price: { $lt: 50 }}
		]
	}
});

// Find orders which are not delivered, not paid, and not cancelled
await swell.get('/orders', {
	where: {
		$nor: [
      { delivered: true },
      { paid: true },
      { status: 'cancelled' }
    ]
	}
});
```

#### Array operators

Array operators are used to query and update records based on items in an array field.

| Name | Description |
| --- | --- |
| $all | Matches arrays that contain all elements specified in the query. |
| $elemMatch | Selects records if element in the array field matches all the specified $elemMatch conditions. |
| $size | Selects records if the array field is a specified size. |

**Array operator examples**

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

// Find products with the tags "consumable" and "organic"
await swell.get('/products', {
	where: {
		tags: { $all: ['consumable', 'organic'] }
	}
});

// Find orders that have a specific coupon applied
await swell.get('/orders', {
	where: {
		discounts: {
      $elemMatch: {
        source_id: '634701a8d394db00135df1bc'
      }
    }
	}
});

// Find orders that have a specific promotion applied
await swell.get('/orders', {
	where: {
		discounts: {
      $elemMatch: {
        type: 'promo-62bc63e193cb7c0019423b6e'
      }
    }
	}
});

// Find orders that include two particular products
await swell.get('/orders', {
	where: {
		items: {
      $all: [
        { $elemMatch: { product_id: '628ba6011869c10019b41f70' }},
        { $elemMatch: { product_id: '628ba442499bba0019b1a96d' }}
      ]
    }
	}
});

// Find orders that include either of two particular products as a subscription
await swell.get('/orders', {
	where: {
		items: {
      $elemMatch: {
        product_id: {
          $in: [ '62b1e30767145000197b2bbf', '62b1dfc4d9dce40019a65797' ]
        },
				'purchase_option.type': 'subscription'
      }
    }
	}
});

// Find orders that only have one item
await swell.get('/orders', {
	where: {
		items: { $size: 1 }
	}
});
```

### Example request

**Example query using where**


## Sorting

Use the `sort` parameter to sort results. The format consists of `<field> <direction>`. Direction is recognized as a case-insensitive word like `ascending` and `descending`, or as abbreviated `asc` and `desc`.

You can combine multiple sort fields in a single comma-separated string, with the first taking precedence in order. The sort value must be a string—array values are ignored.

By default, list results are sorted by `id desc` (newest first). Any direction word that doesn't start with `desc` is treated as ascending.

### Example request

**Example query using sort**

**cURL**

```bash
$ curl https://api.swell.store/products?sort=name+asc
  -u store-id:secret-key
```

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

await swell.get('/products', {
  sort: 'name asc', // or desc
});
```

**PHP**

```php
<?php $swell = new \\Swell\\Client('store-id', 'secret-key');

$swell->get('/products', [
  'sort' => 'name asc' // or desc
]);
```


## Limiting

Use the `limit` parameter to determine how many records should be returned in a result, up to 1000. Defaults to 15. Optionally use in combination with `page` to perform pagination.

### Example request

**Example query using limit**

**cURL**

```bash
$ curl https://api.swell.store/products?limit=100
  -u store-id:secret-key
```

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

await swell.get('/products', {
  limit: 100,
});
```

**PHP**

```php
<?php $swell = new \\Swell\\Client('store-id', 'secret-key');

$swell->get('/products', [
  'limit' => 100
]);
```


## Pagination

Use the `page` parameter to determine which page of results to return, relative to the default or specified `limit`. Defaults to 1.

Use `skip` to offset results directly—when set, it overrides the offset normally computed as `(page - 1) × limit`.

Two special values are supported: `page=all` removes the limit and returns all matching records in one response, and `page=false` disables pagination entirely, returning a plain array of records without the usual response envelope.

Paginated responses include `count` (total matching records), `results`, `page`, `limit`, `page_count`, and a `pages` map with the start and end record numbers of each page. The `pages` map is left out when the results fit on one page. It covers a window of pages around the current page, 10 by default; use `window` to change it. With `page=all`, every matching record is returned, and `limit` and `page_count` still reflect the default page size.

```json
{
  "count": 42,
  "results": [
    { "...": "..." }
  ],
  "page": 2,
  "limit": 15,
  "page_count": 3,
  "pages": {
    "1": { "start": 1, "end": 15 },
    "2": { "start": 16, "end": 30 },
    "3": { "start": 31, "end": 42 }
  }
}
```

### Example request

**Example query using page**

**cURL**

```bash
$ curl https://api.swell.store/products?page=2
  -u store-id:secret-key
```

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

await swell.get('/products', {
  page: 2,
});
```

**PHP**

```php
<?php $swell = new \\Swell\\Client('store-id', 'secret-key');

$swell->get('/products', [
  'page' => 2
]);
```


## Searching

Use the `search` parameter to perform a text search on a model's searchable fields. This is a basic implementation using case-insensitive regular expressions: each word in the query matches as a substring, and all words must match within the same field. For example, searching `blue shirts` matches records where a single searchable field contains both "blue" and "shirt"—including partial words, so "shirt" also matches "t-shirts". Wrap a phrase in double quotes to match it exactly, including spaces.

The models below define dedicated search fields. All other models are searched across every string field, including fields nested in objects and arrays.

> **Tip:** For building a user-facing store search experience, we recommend using Algolia, Typesense, or Meilisearch as they provide far more powerful and customizable search features.

| Model | Searchable fields |
| --- | --- |
| /accounts | name, email, phone, vat_number, notes |
| /products | name, slug, sku |
| /products:variants | name, sku |
| /carts | number, billing.name, shipping.name |
| /orders | number, billing.name, shipping.name |
| /categories | name, slug |
| /purchaselinks | name |
| /shipments | id, order_id, tracking_code, packages.tracking_code, destination.name, destination.address1 |
| /returns | id, order_id, tracking_code, origin.name, origin.address1 |

### Example request

**Example query using search**

**cURL**

```bash
$ curl https://api.swell.store/products?search=blue+shirts
  -u store-id:secret-key
```

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

await swell.get('/products', {
  search: 'blue shirts',
});
```

**PHP**

```php
<?php $swell = new \\Swell\\Client('store-id', 'secret-key');

$swell->get('/products', [
  'search' => 'blue shirts'
]);
```


## Expanding

Expanding allows you to include related records as defined by `link` or `collection` fields. For example, in [the cart model](https://developers.swell.is/backend-api/carts/the-cart-model), items are linked to a product with the `product` field which means they aren't automatically included in the result when retrieving a cart. Since the item product is nested in a list of objects, you would use dot-notation to specify the path to the expandable field.

When the expandable field refers to many records, you may want to specify a limit to include more than the default limit, which is 5. To do that for example, use the format `variants:50` with the field name and limit separated by `:`.

When retrieving a list of results, `expand` will be performed on all records in the result set.

Expand paths can be at most 5 levels deep—deeper queries return an error.

### Example request

**Example query using expand**

**cURL**

```bash
$ curl https://api.swell.store/carts/5407e0929fe97f9d4c712a5e?expand=items.product,items.variant
  -u store-id:secret-key
```

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

await swell.get('/carts/{id}', {
  id: '5407e0929fe97f9d4c712a5e',
  expand: [
    'items.product',
    'items.variant',
  ],
});
```

**PHP**

```php
<?php $swell = new \\Swell\\Client('store-id', 'secret-key');

$swell->get('/carts/{id}', [
  'id' => '5407e0929fe97f9d4c712a5e',
  'expand' => [
    'items.product',
    'items.variant'
  ]
]);
```

### Example response

```json
{
  "id": "5407e0929fe97f9d4c712a5e",
  "items": [
    {
      "id": "5407e0929fe97f9d4c712a5f",
      "product_id": "5407e0929fe97f9d4c712a5g",
      "product": {
        "name": "Example product",
        ...
      }
    },
    {...}
  ],
  ...
}
```


## Including

Use the `include` parameter to include results in a query that may or may not be related to the records being retrieved. For example, when you know there's a need to retrieve two similar records at a time in order to reduce the number of API calls a page would need.

Use `include.params` to specify relative values to the records returned by the main query. The actual field values will be substituted by the API. Use `include.data` to specify literal values for filtering the included result.

An include can also define a `conditions` object evaluated against each parent record—the included query only runs for records that match.

Each key in the `include` object names the field the result is attached to, and each entry needs a `url`. On a list query the included query runs once per record, and its result is attached to that record. A record receives `null` when its `conditions` don't match, or when a placeholder in the `url` can't be resolved from the record.

**Include examples**

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

// Include a second, unrelated query alongside the main result
await swell.get('/products', {
  limit: 25,
  include: {
    on_sale: {
      url: '/products',
      data: {
        limit: 5,
        where: { sale: true }
      }
    }
  }
});

// Use params to pass a value from each record into the included query.
// Here each account's id is used to fetch that account's orders.
await swell.get('/accounts', {
  limit: 25,
  include: {
    orders: {
      url: '/orders',
      params: { account_id: 'id' },
      data: {
        limit: 5,
        fields: 'number, grand_total'
      }
    }
  }
});

// Substitute a record value directly into the url
await swell.get('/orders', {
  limit: 25,
  include: {
    customer: {
      url: '/accounts/{account_id}',
      data: {
        fields: 'name, email'
      }
    }
  }
});

// Run the include only for records that match conditions.
// Accounts with no orders receive `orders: null`.
await swell.get('/accounts', {
  limit: 25,
  include: {
    orders: {
      url: '/orders',
      params: { account_id: 'id' },
      conditions: {
        order_count: { $gt: 0 }
      },
      data: { limit: 5 }
    }
  }
});

// Include more than one query in the same request
await swell.get('/accounts', {
  limit: 25,
  include: {
    orders: {
      url: '/orders',
      params: { account_id: 'id' },
      data: { limit: 5 }
    },
    subscriptions: {
      url: '/subscriptions',
      params: { account_id: 'id' },
      data: { limit: 5 }
    }
  }
});
```

Each include adds 1 point to the request's weight, and an include nested inside another include's `data` adds its own. See [Rate limits](https://developers.swell.is/backend-api/rate-limits) for how weight is counted.

→ See the [Advanced queries guide](https://developers.swell.is/guides/advanced-queries) for a worked example using `include` with `params` and `data`.

### Example request

**Example query using `include`**

**cURL**

```bash
$ curl https://api.swell.store/orders/5407e0929fe97f9d4c712a5f?include[fulfilled_giftcards][url]=/giftcards&include[fulfilled_giftcards][params][order_id]=id&include[fulfilled_giftcards][data][balance][$gt]=0
  -u store-id:secret-key
```

**Node**

```javascript
const { swell } = require('swell-node');
swell.init('store-id', 'secret-key');

await swell.get('/orders/{id}', {
  id: '5407e0929fe97f9d4c712a5f',
  include: {
    fulfilled_giftcards: {
      url: '/giftcards',
      params: {
        order_id: 'id',
      },
      data: {
        balance: { $gt: 0 },
      },
    },
  },
});
```

**PHP**

```php
<?php $swell = new \\Swell\\Client('store-id', 'secret-key');

$swell->get('/orders/{id}', [
  'id' => '5407e0929fe97f9d4c712a5f',
  'include' => [
    'fulfilled_giftcards' => [
      'url' => '/giftcards',
      'params' => [
        'order_id' => 'id'
      ],
      'data' => [
        'balance' => [ '$gt' => 0 ]
      ]
    ]
  ]
]);
```

### Example response

```json
{
  "id": "5407e0929fe97f9d4c712a5f",
  "fulfilled_giftcards": [
    {
      "id": "5407e0929fe97f9d4c712a5g",
      "balance": 25
    },
    {...}
  ],
  ...
}
```

