◂ Retour à [README](./README.md)

# inwink JSON Query Language

The query language is inspired by MongoDB syntax. Expressions are JSON objects where `$`-prefixed keys are operators and non-prefixed keys are entity property names.

Utilisé par les `filters`, `orders` et `selects` des opérations [query](./query.md), [update](./update.md) (massupdate) et [delete](./delete.md) (massdelete).

## Predicate Expressions (filters)

### Comparison operators

| Operator | Aliases | Meaning |
|---|---|---|
| `$eq` | `$equal` | Equals |
| `$neq` | `$notEqual` | Not equals |
| `$gt` | `$greaterThan` | Greater than |
| `$gte` | `$greaterThanOrEqual` | Greater than or equal |
| `$lt` | `$lessThan` | Less than |
| `$lte` | `$lessThanOrEqual` | Less than or equal |

**Implicit equality:** `{ "p1": "v1" }` is equivalent to `{ "p1": { "$eq": "v1" } }`.

### Logical operators

- `$and`: `{ "$and": [ { "p1": "v1" }, { "p2": "v2" } ] }`
- `$or`: `{ "$or": [ { "p1": "v1" }, { "p2": "v2" } ] }`
- `$not`: negates a predicate

### Text operators (under `$text` namespace)

| Operator | Example | Returns |
|---|---|---|
| `$contains` | `{ "p1": { "$text.$contains": "val" } }` | true if p1 contains "val" |
| `$startsWith` | `{ "p1": { "$text.$startsWith": "val" } }` | true if p1 starts with "val" |
| `$endWith` | `{ "p1": { "$text.$endWith": "val" } }` | true if p1 ends with "val" |
| `$indexOf` | `{ "p1": { "$text.$indexOf": "val" } }` | position of first occurrence, -1 if none |
| `$substring` | `{ "p1": { "$text.$substring": { "$startIndex": 1, "$length": 3 } } }` | substring extraction |
| `$length` | `{ "p1": { "$text.$length": {} } }` | length of the string |

Alternate syntax: `{ "p1": { "$text": { "$contains": "val" } } }` is equivalent.

### Collection operators

- `$any`: `{ "relateds": { "p1": "v1" } }` — true if at least one item in the collection matches.
- `$all`: true if every item in the collection matches.

### Date and time operators

| Operator | Purpose |
|---|---|
| `$now` | Current date/time |
| `$utcNow` | Current UTC date/time |
| `$days`, `$hours`, `$minutes`, `$seconds`, `$milliseconds`, `$ticks`, `$timespan` | Duration construction |

**Example — entities modified in the last 24 hours:**

```json
{ "validFrom": { "$gt": { "$utcNow": { "$substract": { "$days": 1 } } } } }
```

### Arithmetic operators

`$add`, `$substract`, `$multiply`, `$divide`, `$modulo`

**Example — sum of two properties:** `{ "p1": { "$add": { "p2": {} } } }`

## Value Expressions

A property is referenced by nesting JSON keys terminated with `{}`:

- `{ "p1": {} }` → value of `p1`
- `{ "p1": { "p2": {} } }` → value of `p1.p2` (deep access)

## Verbose Binary Operator Syntax

When fluent chaining doesn't work (e.g. when an operand is a terminal expression), use:

```json
{ "$operator": { "$left": { ... }, "$right": { ... } } }
```

## Ordering

The `orders` array contains objects with `desc` (boolean) and `value` (a value expression):

```json
{
  "orders": [
    { "desc": true, "value": { "validFrom": {} } }
  ]
}
```

## Projection Expressions (selects)

```json
{ "$all": true, "secretField": false }
```

- `true` = include, `false` = exclude.
- `$all` sets the default for all properties.
- Nested objects define joins/expands: `{ "sessions": { "$all": true } }`

## Localizable Properties and `$meta`

Some properties (like `title`, `description`) are localized objects: `{ "fr": "...", "en": "..." }`. By default, operations apply to the event's default language.

To force a specific language, wrap the expression in `$meta`:

```json
{
  "$meta": {
    "lang": "en",
    "$token": { "title": { "$text.$contains": "search term" } }
  }
}
```

Nested `$meta` operators are cumulative; the closest ancestor `$meta` wins when a localizable property is evaluated.
