## Basic Query Syntax

The `query` has the same syntax with query string on MSP web UI, the syntax is illustrated below:
```bash
query = search-term [ " " search-term ]*
search-term = literal-search | numeric-search
literal-search = [ [ "-" ] qualifier ":" ] literal-match
qualifier = <property-path> | <property-alias>
literal-match = literal [ "," literal ]*
literal = <string> | <quoted-string>
numeric-search = qualifier ":" numeric-match
numeric-match = [ ">" | ">=" | "<" | "<=" ] <number> [ <unit> ] | <number> [ <unit> ] "-" <number> [ <unit> ]
```

- Each `query` is composed of one or multiple space-separated `search-term`.
- Each `search-term` represents one condition that the result has to meet. It's either a [`literal-search`](#literal-search) or a [`numeric-search`](#numeric-search)
- **IMPORTANT** The query string must be URL encoded on sending

An example for a full query
```
box.name:"Gold Plus",Purple mac:"AA:BB:CC:DD:EE:FF" Total:>50MB
```

### Qualifier

A `qualifier` specifies the property that supposed to be matched with the match provided. It could be represented in form of a dot-separated path, like `device.name`, which follows the data model of resource being searched. A pre-defined `alias` could also be used as qualifier. Alias is case insensitive. Not all properties have alias.

A `search-term` without qualifier is an unqualified search. It searches across a subset of properties. The subset being searched is listed with each resource type.

### Literal Search

A literal search by default checks if a property value equals to the literal provided. Multiple literals could be provided with `,` in between. Results matching either of them are returned. Literal search works only on string properties. Literal search is case sensitive.

#### Wildcard

The wildcard `*` could be used to perform a fuzzy search, which could come handy in various ways. For example, `device.name:*iphone*` returns `iphone-12`, `joe-iphone`, and any other device that name contains `iphone`.
Wildcard search does not currently support [unqualified search](#qualifier) or [exclusive search](#exclusive-search).

#### Quoted Search

To search for a string that contains whitespace `,` `*` `:`, the string has to be quoted with double quotes `""`. For example:
```json
box:"Firewalla GSE","Firewalla,GSE"
```

To search for a string that contains `"` `\` `*`, the string has to be quoted and all `"` `\` `*` in string has be to escaped with a backslash `\`. For example
```json
box:"\"fire","Fire\\walla:GSE","Firewalla Gold\*"
```

#### Exclusive Search

You can narrow down search results by excluding one or more subsets. To exclude all results that are matched by a `search-term`, prefix it with a hyphen `-`. For example, the following statement excludes any alarms with the status 'active'<br>
```json
-status:active
```

### Numeric Search

A numeric search by default checks if a property value matches a specific mathematical relation to the number provided. By default it checks for equality, while `>` `>=` `<` or `<=` could be inserted before the number to denote other relations.

Numeric search only works for numeric properties. It supports neither unqualified search nor exclusive search.

| Operator | Description |
| -------- | ------- |
| `:<` | Less than, only works for numeric properties,  <br>  `download:<10KB` or `download:<10000` |
| `:>` | Greater than, only works for numeric properties <br>  `upload:>10KB` or `upload:>10000` |
| `:<=` | Less than or equal to, only works for numeric properties,  <br>  `download:<=10KB` or `download:<=10000` |
| `:>=` | Greater than or equal to, only works for numeric properties <br>  `upload:>=10KB` or `upload:>=10000` |

##### Range Search

You can use the range syntax `n-m` to search for values within a range, where the first number n is the lower bound and the second one m is the higher bound. e.g. `download:1000-2000` or `ts:1695196894.395-1695604487.633`

##### Unit

Some property could be searched with a suffixed unit. The available units and convention is documented with corresponding property.

### Pagination

Instead of returning a huge chunk of data in a request, pagination divides and returns data to clients in smaller batches, which is easier for both sides to deal with. Every paged response, except the last one, is returned with a base64 encoded `next_cursor`. And every request, except the very first one, is required to set `cursor` with corresponding value returned from server earlier.

Example

```javascript
const params = {
    query: `status:active box:${box}`,
    cursor: null,
    limit: 10
}
const alarms = [];

while (1) {
    const { results, next_cursor } = await httpClient({
        method: 'get',
        url: `/alarms`,
        params: params
    }).then(r => r.data);
    alarms.push(...results);
    if (!next_cursor) break;
    params.cursor = next_cursor;
}
```

## Alarm Qualifiers

You can search [Alarm](../data-models/alarm) with the following [qualifiers](#qualifier).

| Qualifier | Example and Additional Notes |
| ------------ | -------------------- |
| **ts** | <span style="color:white;font-family:Lucida Console,Monospace">ts:<1695196894.395</span> <br> The timestamp of alarm. This is usually used together with [sortBy](alarm/#get-alarms) to perform a desired search |
| **type** <br><span style="color:grey">*alias:*  AlarmType</span>| <span style="color:white;font-family:Lucida Console,Monospace">type:1,2,3 <br><span style="color:grey"> AlarmType:"Security Activity,Abnormal Upload,Large Bandwidth Usage"</span></span> |
| **status** | <span style="color:white;font-family:Lucida Console,Monospace">status:active</span> |
| **box.id** | <span style="color:white;font-family:Lucida Console,Monospace">box.id:00000000-0000-0000-0000-000000000000</span> <br> the box object is not returned in the search result while only gid (box.id) is returned, this is by design |
| **box.name** <br><span style="color:grey">*alias:* Box</span>| <span style="color:white;font-family:Lucida Console,Monospace">box.name:FirewallaGold</span> |
| **box.group.id** | <span style="color:white;font-family:Lucida Console,Monospace">box.group.id:1</span> the unique id of the msp group |
| **device.id** <br><span style="color:grey">*alias:*  Mac</span>| <span style="color:white;font-family:Lucida Console,Monospace">device.id:"AA:BB:CC:DD:EE:FF"</span>|
| **device.name** <br><span style="color:grey">*alias:*  Device</span>| <span style="color:white;font-family:Lucida Console,Monospace">device.name:iphone</span>|
| **device.network.id** | <span style="color:white;font-family:Lucida Console,Monospace">device.network.id:00000000-1111-1111-1111-000000000000</span> |
| **device.network.name** <br><span style="color:grey">*alias:*  Network</span>| <span style="color:white;font-family:Lucida Console,Monospace">device.network.name:Guest</span> |
| **remote.category** <br><span style="color:grey">*alias:*  Category</span> | <span style="color:white;font-family:Lucida Console,Monospace">remote.category:porn,game</span> |
| **remote.domain** <br><span style="color:grey">*alias:*  Domain</span> | <span style="color:white;font-family:Lucida Console,Monospace">remote.domain:google.com</span> |
| **remote.region** <br><span style="color:grey">*alias:*  Region</span>  | <span style="color:white;font-family:Lucida Console,Monospace">remote.region:US</span> |
| **transfer.download** <br><span style="color:grey">*alias:*  Download</span> | <span style="color:white;font-family:Lucida Console,Monospace">transfer.download:>10MB</span><br> [Available Units](#units) |
| **transfer.upload** <br><span style="color:grey">*alias:*  Upload</span> | <span style="color:white;font-family:Lucida Console,Monospace">transfer.upload:>10MB</span><br> [Available Units](#units)|
| **transfer.total** <br><span style="color:grey">*alias:*  Total</span> | <span style="color:white;font-family:Lucida Console,Monospace">transfer.total:>50MB</span><br> [Available Units](#units) |

## Flow Qualifiers

You can search [Flow](../data-models/flow) with the following [qualifiers](#qualifier).

| Qualifier | Example and Additional Notes |
| ------------ | -------------------- |
| **ts** | <span style="color:white;font-family:Lucida Console,Monospace">ts:<1695196894.395</span> <br> The timestamp of flow. This is usually used together with [sortBy](flow/#get-flows) to perform a desired search |
| **status** | <span style="color:white;font-family:Lucida Console,Monospace">status:ok</span> |
| **direction** | <span style="color:white;font-family:Lucida Console,Monospace">direction:outbound</span> |
| **box.id** | <span style="color:white;font-family:Lucida Console,Monospace">box.id:00000000-0000-0000-0000-000000000000</span> <br> the box object is not returned in the search result while only gid (box.id) is returned, this is by design |
| **box.name** <br><span style="color:grey">*alias:* Box</span>| <span style="color:white;font-family:Lucida Console,Monospace">box.name:FirewallaGold</span> |
| **box.group.id** | <span style="color:white;font-family:Lucida Console,Monospace">box.group.id:1</span> the unique id of the msp group |
| **device.id** <br><span style="color:grey">*alias:*  Mac</span>| <span style="color:white;font-family:Lucida Console,Monospace">device.id:"AA:BB:CC:DD:EE:FF"</span>|
| **device.name** <br><span style="color:grey">*alias:*  Device</span>| <span style="color:white;font-family:Lucida Console,Monospace">device.name:iphone</span>|
| **network.id** | <span style="color:white;font-family:Lucida Console,Monospace">network.id:00000000-1111-1111-1111-000000000000</span> |
| **network.name** <br><span style="color:grey">*alias:*  Network</span>| <span style="color:white;font-family:Lucida Console,Monospace">network.name:Guest</span> |
| **category** <br><span style="color:grey">*alias:*  Category</span> | <span style="color:white;font-family:Lucida Console,Monospace">category:porn,game</span> |
| **domain** <br><span style="color:grey">*alias:*  Domain</span> | <span style="color:white;font-family:Lucida Console,Monospace">domain:google.com</span> |
| **region** <br><span style="color:grey">*alias:*  Region</span>  | <span style="color:white;font-family:Lucida Console,Monospace">region:US</span> |
| **sport** <br><span style="color:grey">*alias:*  SourcePort</span> | <span style="color:white;font-family:Lucida Console,Monospace">sport:123</span> |
| **dport** <br><span style="color:grey">*alias:*  DestinationPort</span> | <span style="color:white;font-family:Lucida Console,Monospace">dport:123</span> |
| **download** <br><span style="color:grey">*alias:*  Download</span> | <span style="color:white;font-family:Lucida Console,Monospace">download:>10MB</span><br> [Available Units](#units) |
| **upload** <br><span style="color:grey">*alias:*  Upload</span> | <span style="color:white;font-family:Lucida Console,Monospace">upload:>10MB</span><br> [Available Units](#units)|
| **total** <br><span style="color:grey">*alias:*  Total</span> | <span style="color:white;font-family:Lucida Console,Monospace">total:>50MB</span><br> [Available Units](#units) |

## Rule Qualifiers

You can search [Rule](../data-models/rule) with the following [qualifiers](#qualifier).

| Qualifier | Example and Additional Notes |
| ------------ | -------------------- |
| **status** | <span style="color:white;font-family:Lucida Console,Monospace">status:active</span> |
| **action** | <span style="color:white;font-family:Lucida Console,Monospace">action:block</span> |
| **box.id** | <span style="color:white;font-family:Lucida Console,Monospace">box.id:00000000-0000-0000-0000-000000000000</span> <br> the box object is not returned in the search result while only gid (box.id) is returned, this is by design |
| **box.group.id** | <span style="color:white;font-family:Lucida Console,Monospace">box.group.id:1</span><br> the unique id of the msp group |
| **device.id** | <span style="color:white;font-family:Lucida Console,Monospace">device.id:"AA:BB:CC:DD:EE:FF"</span> <br> the device object is not returned in the search result while only nested object `scope:{"type":"device","value":"AA:BB:CC:DD:EE:FF"}` is returned, this is by design |

### Units
```
 B (Byte)
KB (KiloByte) = 1000 B
MB (MegaByte) = 1000 KB 
GB (GigaByte) = 1000 MB
TB (TeraByte) = 1000 GB
```
