# Search Language Guide

*<span class="">A plain-language reference for finding, shaping and retrieving objects across all your connected systems.</span>*

---

## <span class="">1. About this guide</span>

<span class="">This guide explains the search language that your product uses to look for objects across every connected system (</span>*<span class="">provider</span>*<span class="">) at once. It is written so that anyone — not just developers — can read, understand and write searches.</span>

- **<span class="">Sections 3–5</span>**<span class=""> cover everything a normal user needs day to day.</span>
- **<span class="">Sections 6–7</span>**<span class=""> cover settings and advanced constructs that admins and power users will occasionally need.</span>
- **<span class="">Sections 8–9</span>**<span class=""> are a quick reference and a cookbook of examples.</span>

<span class="">Wherever a search is shown, it appears in a code block like this:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] SORT BY :createdAt DESC LIMIT 20
```

---

## <span class="">2. Very short refresher</span>

<span class="">You can skip this section if you already know the product, but here is a one-page reminder of the vocabulary used throughout:</span>

- **<span class="">Object</span>**<span class=""> — a single record of data (a document, a product, a user, etc). Every object has an </span>**<span class="">ID</span>**<span class="">.</span>
- **<span class="">Type</span>**<span class=""> — a category of object, such as </span>`Document`<span class="">, </span>`Word Document`<span class=""> or </span>`Folder`<span class="">. Types form a hierarchy: a </span>`Word Document` *<span class="">is a</span>* `Document`<span class=""> and a </span>`Document` *<span class="">is a</span>* `File`<span class="">, which is an </span>`Object`<span class="">. A subtype inherits all the properties of its parent types.</span>
- **<span class="">Property</span>**<span class=""> — a named field on an object, such as </span>`name`<span class="">, </span>`createdAt`<span class=""> or </span>`x`<span class="">. Searches refer to properties with a leading colon: </span>`:name`<span class="">.</span>
- **<span class="">Relation</span>**<span class=""> — a property whose value points at another object's ID (or a list of IDs). Relations let objects be connected to each other.</span>
- **<span class="">Provider</span>**<span class=""> — a connected system or database. Each provider has its own ID. An object ID looks like </span>`local-id@provider-id`<span class="">; for example, </span>`54c25070abe654a14867226e@0`<span class=""> lives in provider </span>`0`<span class="">.</span>
- **<span class="">Current user</span>**<span class=""> — the person running the search. The language has a shortcut for "me" and "my groups" (see §4.10).</span>

<span class="">A single search is executed against </span>**<span class="">all providers in parallel</span>**<span class=""> (by default), so you never need to know which system owns which object.</span>

---

## <span class="">3. Anatomy of a search</span>

<span class="">Every search has three logical parts, in this order:</span>

1. **<span class="">A filter</span>**<span class=""> — </span>*<span class="">what</span>*<span class=""> you are looking for. Written as a chain of conditions. Example: </span>`[:status "Active"]`<span class="">.</span>
2. **<span class="">Zero or more shaping steps</span>**<span class=""> — </span>*<span class="">how</span>*<span class=""> to transform what you found. Sort, join, group, aggregate, limit, flatten, compute mapped values, return related objects.</span>

<span class="">Only the filter is required; every shaping step is optional. A search with no filter at all is valid (for example, </span>`LIMIT 10 SORT BY :createdAt DESC`<span class=""> returns the ten most recent objects of any type).</span>

<span class="">Spacing and capitalization of keywords (</span>`AND`<span class="">, </span>`OR`<span class="">, </span>`JOIN`<span class="">, </span>`SORT BY`<span class="">…) matter.</span>

---

## <span class="">4. Finding things — the filter</span>

<span class="">The filter is written inside square brackets when it's a single condition, or as a chain when there are several. The following rules show how to build filters.</span>

### <span class="">4.1 Search for text anywhere</span>

<span class="">Writing a word by itself searches all text-like properties of every object for that word.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-1"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
laptop
```

<span class="">This is a </span>**<span class="">keyword / full-text</span>**<span class=""> search by default (see §4.14 to choose semantic search instead).</span>

<span class="">A quoted phrase searches for the phrase as a whole:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-2"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
"folding laptop stand"
```

<span class="">You can combine several bare words; they are treated as </span>`AND`<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-3"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
laptop stand
```

<span class="">is the same as </span>`laptop AND stand`<span class="">.</span>

> <span class="">Anywhere text appears in quotes you may escape a double-quote with </span>`\"`<span class=""> and a backslash with </span>`\\`<span class="">. No other escape sequences are supported.</span>

### <span class="">4.2 “Does this property exist?”</span>

<span class="">Wrapping a property in brackets with nothing else asks: </span>*<span class="">“does this object have any value for this property?”</span>*

<div class="md-code-block md-code-block-dark" id="bkmrk-text-4"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:email]
```

<span class="">matches every object that has an </span>`email`<span class=""> property. It's a handy way to filter by shape rather than by value.</span>

### <span class="">4.3 Compare a property with a value</span>

<span class="">Most filters take the form </span>`[:property operator value]`<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-5"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price < 100]
[:status "Active"]
[:publishedAt >= 2024-01-01]
[:archived FALSE]
```

<span class="">Supported comparison operators for numerical and date/time values:</span>

<div class="ds-scroll-area ds-scroll-area--show-on-focus-within ds-scroll-area--enabled _1210dd7 c03cafe9" id="bkmrk-operator-meaning-%3D-e"><table><thead><tr><th><span class="">Operator</span></th><th><span class="">Meaning</span></th></tr></thead><tbody><tr><td>`=`</td><td><span class="">Equals</span></td></tr><tr><td>`!=`</td><td><span class="">Not equal</span></td></tr><tr><td>`<`</td><td><span class="">Less than</span></td></tr><tr><td>`>`</td><td><span class="">Greater than</span></td></tr><tr><td>`<=`</td><td><span class="">Less than or equal</span></td></tr><tr><td>`>=`</td><td><span class="">Greater than or equal</span></td></tr></tbody></table>

</div>> <span class="">There is one more operator, </span>`~=`<span class="">, that applies only to relations. It is described in §7.1.</span>

<span class="">You can also compare two properties on the same object directly:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-6"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:startedAt < :finishedAt]
```

<span class="">matches objects where the start date is before the finish date.</span>

### <span class="">4.4 Text inside a specific property</span>

<span class="">To restrict a text search to one property, put the property first and the text second:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-7"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:name laptop]
[:description "folding stand"]
```

<span class="">You can also mix wildcards with the property (see §4.6).</span>

### <span class="">4.5 Wildcards</span>

<span class="">Unquoted text supports two wildcards:</span>

- `*`<span class=""> — matches </span>**<span class="">zero or more</span>**<span class=""> characters.</span>
- `?`<span class=""> — matches </span>**<span class="">exactly one</span>**<span class=""> character.</span>

<span class="">Examples:</span>

<div class="ds-scroll-area ds-scroll-area--show-on-focus-within ds-scroll-area--enabled _1210dd7 c03cafe9" id="bkmrk-query-matches-lapt%2A-"><div class="ds-scroll-area__gutters"><div class="ds-scroll-area__horizontal-gutter">  
</div><div class="ds-scroll-area__vertical-gutter">  
</div></div><table><thead><tr><th><span class="">Query</span></th><th><span class="">Matches</span></th></tr></thead><tbody><tr><td>`lapt*`</td><td>`laptop`<span class="">, </span>`laptops`<span class="">, </span>`lapt`</td></tr><tr><td>`lap?op`</td><td>`laptop`<span class="">, </span>`lapsop`</td></tr><tr><td>`[:sku ABC-*]`</td><td><span class="">any SKU starting with </span>`ABC-`</td></tr><tr><td> </td><td> </td></tr></tbody></table>

</div>> 

### <span class="">4.6 Lists and ranges</span>

<span class="">A property can be compared against a list of values using parentheses:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-8"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status ( "active", "draft", "review" )]
[:size ( 1, 20, 3 )]
[:price ( 10 TO 100 )]
```

- `( a, b, c )`<span class=""> — matches if the property equals </span>**<span class="">any</span>**<span class=""> of the values.</span>
- `( a TO b )`<span class=""> — matches if the property is </span>**<span class="">between</span>** `a`<span class=""> (inclusive) and </span>`b` (exclusive)<span class="">.</span>

### <span class="">4.7 Dates and times</span>

<span class="">A date value can be written as one of:</span>

- <span class="">A named date: </span>`TODAY`<span class="">, </span>`THIS WEEK`<span class="">, </span>`THIS MONTH`<span class="">, </span>`THIS YEAR`<span class="">, </span>`NOW`<span class="">.</span>
- <span class="">An offset from any of the above</span><span class="">: `TODAY`</span>` - 1 MONTH`<span class="">, </span>`NOW + 3 HOURS`, `THIS YEAR - 2 YEARS`.
- <span class="">An ISO date or datetime: </span>`2024-03-15`<span class="">, </span>`2024-03-15T14:30:00`<span class="">, </span>`2024-03-15T14:30:00.000+02:00`<span class="">.</span>

<span class="">Offsets accept these units:</span>

`MILLIS`<span class="">, </span>`SECOND(S)`<span class="">, </span>`MINUTE(S)`<span class="">, </span>`HOUR(S)`<span class="">, </span>`DAY(S)`<span class="">, </span>`WEEK(S)`<span class="">, </span>`MONTH(S)`<span class="">, </span>`YEAR(S)`<span class="">.</span>

<span class="">Examples:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-9"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:createdAt >= TODAY]
[:dueAt < NOW + 1 WEEK]
[:updatedAt >= 2024-01-01]
[:startsAt < NOW - 24 HOURS]
```

<span class="">Anything that is not one of the forms above is treated as a mistake and the search will fail.</span>

> <span class="">The property you compare against must be a date or datetime property.</span>

### <span class="">4.8 Types and inheritance</span>

<span class="">To match objects </span>*<span class="">of a type</span>*<span class=""> or any of its subtypes, use </span>`INHERITS`<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-10"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:types INHERITS "Folder"]
[:types INHERITS ID "54c25070abe654a14867226e@0"]
```

<span class="">Both forms match if the object's </span>`:types`<span class=""> value is of the given type </span>**<span class="">or any type derived from it</span>**<span class="">. The string form uses the type name; the </span>`ID`<span class=""> form uses the type's object ID.</span>

<span class="">You can also use </span>`INHERITS`<span class=""> inside a list, so that a single filter accepts both specific objects and whole type trees:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-11"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:parent ( ID "aaa@0", INHERITS ID "bbb@0", INHERITS "Folder" )]
```

<span class="">This matches an object whose </span>`:parent`<span class=""> is either the specific object </span>`aaa@0`<span class="">, or any object whose type is </span>`bbb@0`<span class=""> (or a subtype), or any </span>`Folder`<span class=""> (or subtype).</span>

### <span class="">4.9 “Me” and “my groups”</span>

<span class="">Two shortcuts refer to the current user:</span>

- `ME`<span class=""> — the current user's own object.</span>
- `MY GROUPS`<span class=""> — any group the current user belongs to.</span>

<span class="">Used with a property, they say “this relation points at me / at one of my groups”:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-12"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:assignee ME]
[:members MY GROUPS]
```

### <span class="">4.10 Arrays — checking length</span>

<span class="">If a property holds an array, </span>`LENGTH`<span class=""> compares how many items it has:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-13"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:tags LENGTH > 5]
[:path LENGTH = 2]
```

<span class="">Only meaningful with </span>`>`<span class="">, </span>`>=`<span class="">, </span>`=`<span class="">, </span>`!=`<span class="">, </span>`<`<span class="">, </span>`<=`<span class=""> against a </span>**<span class="">number</span>**<span class="">.</span>

### <span class="">4.11 Nested properties</span>

<span class="">You can walk into nested data with dots:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-14"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:owner.age > 20]
[:address.city "Sofia"]
```

<span class="">The same applies in mapped expressions and flatten steps.</span>

### <span class="">4.12 Combining conditions</span>

<span class="">Use </span>`AND`<span class="">, </span>`OR`<span class=""> and </span>`NOT`<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-15"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] AND [:price < 100]
[:status "Active"] OR [:status "Draft"]
NOT [:archived TRUE]
```

<span class="">There are a few shortcuts to keep things short:</span>

- **`AND`<span class=""> is optional when two conditions are adjacent.</span>**<span class=""> These two are the same:</span>
    
    <div class="md-code-block md-code-block-dark"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
    [:status "Active"] [:price < 100]
    [:status "Active"] AND [:price < 100]
    ```
- **<span class="">Brackets group conditions</span>**<span class=""> the same way they do in ordinary arithmetic:</span>
    
    <div class="md-code-block md-code-block-dark"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
    ([:status "Active"] OR [:status "Draft"]) AND [:price < 100]
    ```

<span class="">See §8 for precedence rules.</span>

### <span class="">4.13 Semantic vs keyword</span>

<span class="">By default, bare text is a </span>*<span class="">keyword</span>*<span class=""> search: it looks for the word in indexed text. You can force keyword search explicitly with </span>`KEY`<span class="">, and switch to </span>*<span class="">semantic</span>*<span class=""> search with </span>`SEM`<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-16"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
KEY "user login event"                # keyword / full-text
SEM "user login event"                # semantic
```

`SEM`<span class=""> uses whatever embedding engine and model the system is configured with, stored in a vector database, so results are based on meaning, not literal word match. </span>`KEY`<span class=""> is the ordinary exact-ish match.</span>

`SEM`<span class=""> and </span>`KEY`<span class=""> can be used with quoted text or bare words:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-17"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
SEM "failed payment"
KEY failed
SEM error
```

---

## <span class="">5. Shaping the result — the clauses</span>

<span class="">Each of the clauses below is optional and can appear in roughly any order. The sections are ordered by how often you'll use them.</span>

### <span class="">5.1 Sorting — </span>`SORT BY`

<div class="md-code-block md-code-block-dark" id="bkmrk-text-18"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] SORT BY :createdAt DESC
[:status "Active"] SORT BY :priority ASC, :dueAt DESC
```

- <span class="">Multiple sort fields are comma-separated.</span>
- `ASC`<span class=""> (ascending, the default) and </span>`DESC`<span class=""> (descending) are optional. </span>`SORT BY :x`<span class=""> is the same as </span>`SORT BY :x ASC`<span class="">.</span>
- <span class="">Sorting can also be supplied via an option (see §6) rather than inside the query.</span>

### <span class="">5.2 Limiting — </span>`LIMIT`

<div class="md-code-block md-code-block-dark" id="bkmrk-text-19"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] LIMIT 20
```

<span class="">Caps the number of results at 20. If </span>`LIMIT`<span class=""> appears twice, the last one wins.</span>

### <span class="">5.3 Joining — </span>`JOIN`

<span class="">A </span>`JOIN`<span class=""> pulls in related objects that match an expression, and combines each result with each match.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-20"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] JOIN :owner [:]
```

- `:owner`<span class=""> is the </span>**<span class="">name</span>**<span class=""> you choose for the joined objects.</span>
- <span class="">The </span>`[:]`<span class=""> expression is a filter on the joined objects. Inside this expression, unqualified properties refer to the </span>**<span class="">joined object</span>**<span class="">; use </span>`:default:property`<span class=""> to refer back to the original.</span>
- <span class="">Each match of the joined filter multiplies the original result. An original with two matches appears twice, once for each match.</span>
- <span class="">After the join, refer to the joined object's properties as </span>`:owner:property`<span class="">.</span>

<span class="">You can have several </span>`JOIN`<span class=""> clauses, each with its own name:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-21"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"]
  JOIN :owner    [:role "Manager"]
  JOIN :reviewer [:approved TRUE]
```

**<span class="">Variants:</span>**

- `JOIN ALLOW EMPTY :name [...]`<span class=""> — outer join. The original object is kept even if no joined object matches. Fields from the joined side are simply missing on those rows. (See also ALLOW EMPTY note in §7.7.)</span>
- `NOT JOIN :name [...]`<span class=""> — anti-join. Returns only original objects that have </span>**<span class="">no</span>**<span class=""> matching joined object. See §7.7.</span>

### <span class="">5.4 Flatten — </span>`FLATTEN`

<span class="">If a property holds an array, </span>`FLATTEN`<span class=""> expands each element into its own row.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-22"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price >= 1] FLATTEN :tags GROUP BY :tags AGGREGATE count(:tags) AS :n
```

<span class="">This turns </span>*<span class="">“one row per object, with N tags each”</span>*<span class=""> into </span>*<span class="">“one row per tag”</span>*<span class="">. </span>`FLATTEN`<span class=""> can be given several properties, and an optional filter that applies to the flattened rows:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-23"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price >= 1] FLATTEN :a, :b [:a > 5]
```

<span class="">You can </span>`FLATTEN`<span class=""> more than once, chaining through nested arrays.</span>

### <span class="">5.5 Grouping and aggregation — </span>`GROUP BY`<span class=""> / </span>`AGGREGATE`

<span class="">Aggregation collapses all rows into a small summary.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-24"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price >= 1] AGGREGATE sum(:price) AS :total
```

<span class="">This returns a single row with a </span>`:total`<span class=""> field.</span>

<span class="">To get one row </span>*<span class="">per distinct value</span>*<span class="">, use </span>`GROUP BY`<span class=""> first:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-25"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price >= 1] GROUP BY :status AGGREGATE sum(:price) AS :total AGGREGATE count(:id) AS :n
```

<span class="">Each </span>`AGGREGATE`<span class=""> names an aggregation function, takes one property, and assigns the result to a new property name.</span>

**<span class="">Supported aggregation functions:</span>**

<div class="ds-scroll-area ds-scroll-area--show-on-focus-within ds-scroll-area--enabled _1210dd7 c03cafe9" id="bkmrk-function-meaning-sum"><table><thead><tr><th><span class="">Function</span></th><th><span class="">Meaning</span></th></tr></thead><tbody><tr><td>`sum`</td><td><span class="">Sum of all values</span></td></tr><tr><td>`count`</td><td><span class="">Number of values</span></td></tr><tr><td>`average`</td><td><span class="">Arithmetic mean</span></td></tr><tr><td>`median`</td><td><span class="">Middle value (average of two middles if even)</span></td></tr><tr><td>`min`</td><td><span class="">Smallest value</span></td></tr><tr><td>`max`</td><td><span class="">Largest value</span></td></tr><tr><td>`first`</td><td><span class="">First value in the group</span></td></tr><tr><td>`concat`</td><td><span class="">A list of all values in the group</span></td></tr></tbody></table>

</div><span class="">These are the only functions supported today; they are not user-extensible.</span>

**<span class="">Grouping by time:</span>**

<span class="">For date/datetime properties, </span>`GROUP BY`<span class=""> accepts a time unit that snaps each value to a calendar bucket:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-26"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price >= 1] GROUP BY :createdAt MONTHLY AGGREGATE count(:id) AS :n
```

<span class="">Available units:</span>

`MINUTELY`<span class="">, </span>`HOURLY`<span class="">, </span>`DAILY`<span class="">, </span>`WEEKLY`<span class="">, </span>`MONTHLY`<span class="">, </span>`QUARTERLY`<span class="">, </span>`YEARLY`<span class="">.</span>

<span class="">The bucket value is returned as a formatted string — e.g. </span>`"2020-04"`<span class=""> for </span>`MONTHLY`<span class="">, </span>`"2020-05-15"`<span class=""> for </span>`DAILY`<span class="">, </span>`"2020-05-15 10"`<span class=""> for </span>`HOURLY`<span class="">.</span>

<span class="">Groups with zero rows are still returned (for example if a combination of group fields is missing on every object, you will see a group with a count of 0).</span>

### <span class="">5.6 Mapped properties — </span>`MAP`

`MAP`<span class=""> adds a computed field to each row. Its syntax is:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-27"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
MAP :newProp AS ( expression )
```

<span class="">Examples:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-28"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
MAP :label   AS ( IF :status "Active" THEN "Live" ELSE "Draft" )
MAP :total   AS ( :price + :tax + :shipping )
MAP :ageDays AS ( NOW - :createdAt ) / 86400000
```

<span class="">Rules of thumb:</span>

- <span class="">Inside a </span>`MAP`<span class="">, </span>**<span class="">unqualified properties refer to the </span>*<span class="">original</span>*<span class=""> object</span>**<span class="">. To refer to a property of a joined object, use </span>`:joinName:prop`<span class="">.</span>
- `IF cond THEN a ELSE b`<span class=""> — ternary.</span>
- <span class="">Arithmetic: </span>`+`<span class="">, </span>`-`<span class="">, </span>`*`<span class="">, </span>`/`<span class="">.</span>
- <span class="">A missing property behaves as the </span>*<span class="">identity</span>*<span class=""> value for the operator it's involved in: </span>`0`<span class=""> for </span>`+`<span class="">/</span>`-`<span class="">, </span>`1`<span class=""> for </span>`*`<span class="">/</span>`/`<span class="">. So </span>`:missing * 5`<span class=""> equals </span>`5`<span class=""> and </span>`:x + :missing`<span class=""> equals </span>`:x`<span class="">.</span>
- <span class="">You can chain </span>`MAP`<span class="">s; a later </span>`MAP`<span class=""> can refer to a property added by an earlier one:</span>
    
    <div class="md-code-block md-code-block-dark"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
    MAP :a AS ( :x + :y )
    MAP :b AS ( :a + :z )
    ```
- `MAP FOR SORT :prop AS (...)`<span class=""> says: </span>*<span class="">"compute this value only for the purpose of sorting; don't add it to the returned object."</span>*<span class=""> It behaves identically to </span>`MAP`<span class=""> for the purposes of sorting. Used only for improved performance.</span>

<span class="">Aggregation functions can also appear </span>**<span class="">inside</span>**<span class=""> a </span>`MAP`<span class="">, in which case they summarize the joined objects for each original object:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-29"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"]
  JOIN :child [:kind "Task"]
  MAP :avgChild As ( average(:child:duration) )
```

`CURRENCY_CONVERT`<span class=""> is a special map expression:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-30"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
MAP :usd AS ( CURRENCY_CONVERT( :amount AS USD ) )
```

<span class="">It converts the numeric value to the given currency using the system's conversion rates. See §5.8.</span>

### <span class="">5.7 Returning related objects — </span>`RETURN`

`RETURN` **<span class="">replaces the result set</span>**<span class=""> with the objects pointed to by a relation property.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-31"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] RETURN :attachments
```

<span class="">Here the search that found, say, 10 active documents returns </span>*<span class="">their attachments</span>*<span class=""> instead of the documents themselves. If you list several </span>`RETURN`<span class=""> properties, you get the </span>**<span class="">union</span>**<span class=""> of all of them:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-32"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status = "active"] RETURN :attachments, :comments, :owner
```

`RETURN`<span class=""> can be combined with subsequent </span>`SORT BY`<span class="">, </span>`GROUP BY`<span class="">, </span>`AGGREGATE`<span class=""> and </span>`LIMIT`<span class="">, which then operate on the </span>*<span class="">returned</span>*<span class=""> objects.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-33"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] RETURN :comments SORT BY :createdAt DESC LIMIT 5
```

### <span class="">5.8 Currency conversion — </span>`CURRENCY_CONVERT`

<div class="md-code-block md-code-block-dark" id="bkmrk-text-34"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status = "paid"] MAP :usd AS ( CURRENCY_CONVERT( :amount AS USD ) )
```

- <span class="">First argument: a numeric property on the current row.</span>
- <span class="">Second argument (</span>`AS`<span class="">): the target currency, a three-letter code (</span>`USD`<span class="">, </span>`EUR`<span class="">, </span>`BGN`<span class="">, …).</span>
- <span class="">The system picks up the object's </span>`currency`<span class=""> and </span>`currencyConversionRate`<span class=""> properties to perform the conversion.</span>

---

## <span class="">6. Options — settings outside the query</span>

<span class="">Some settings are not part of the search text itself but are supplied alongside it (for example, by a user interface or an API caller). They affect how the same query behaves:</span>

- **<span class="">Case insensitive</span>**<span class=""> — treat all text as case-insensitive. </span>`"ASD"`<span class=""> and </span>`"asd"`<span class=""> become the same search.</span>
- **<span class="">Skip system objects</span>**<span class=""> — when on, objects whose </span>`isSystem`<span class=""> flag is </span>`true`<span class=""> are excluded from results. When off, system objects are included.</span>
- **<span class="">Search all versions</span>**<span class=""> — by default only the latest version of each object is searched. When on, all versions are considered.</span>
- **<span class="">Diacritic insensitive</span>**<span class=""> — find a letter regardless of how it's written with or without accents/diacritics. For example, an accented </span>`é`<span class=""> and a plain </span>`e`<span class=""> are treated as the same letter. Useful for languages with many diacritics (like French).</span>
- **<span class="">Use synonym dictionary</span>**<span class=""> — when on, the system's global synonym dictionary is consulted, and any of a word's synonyms is also matched.</span>

<span class="">These options apply to any text in the query that is </span>*<span class="">not</span>*<span class=""> a strict comparison — i.e., to bare-word searches and to text searches on a property.</span>

---

## <span class="">7. Advanced</span>

<span class="">The constructs in this section are supported and available to all users, but are less commonly needed. Administrators will use them frequently; ordinary users may see them generated by UI tools and want to know what they mean.</span>

### <span class="">7.1 The </span>`~=`<span class=""> operator</span>

`~=`<span class=""> is the </span>*<span class="">relation-membership</span>*<span class=""> operator. It matches when the sets of related IDs on both sides overlap — that is, when the two sides have </span>**<span class="">at least one common ID</span>**<span class="">.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-35"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:x ~= :y]
```

<span class="">This says: </span>*<span class="">"property </span>`:x`<span class=""> and property </span>`:y`<span class=""> refer to at least one of the same objects."</span>*<span class=""> It's most often used after a </span>`JOIN`<span class="">, when multiple joined objects may share part of their relation with the original object.</span>

`~=`<span class=""> is only meaningful on relation properties; it is not a general text operator.</span>

### <span class="">7.2 Comparing an object by ID, and reverse </span>`INHERITS`

<span class="">Because relations hold IDs, you can compare a property against a specific object's ID:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-36"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:parent = :myFolder]                    # compare with another property
[:author ID "54c25070abe654a14867226e@0"] # compare with a specific object
```

<span class="">The </span>`ID "…"`<span class=""> form works the same way for arrays: </span>`[:author ID "…"]`<span class=""> matches whether </span>`:author`<span class=""> is a single reference or a list of references, provided the target is in there.</span>

<span class="">You can also use inheritance with IDs, which is handy for “who is referenced by this type”:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-37"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:owner INHERITS ID "54c25070abe654a14867226e@0"]
```

<span class="">And there is a </span>**<span class="">reversed</span>**<span class=""> form — read it as </span>*<span class="">“the given type inherits </span>`:property`<span class="">”</span>*<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-38"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
"Folder" INHERITS :types
```

<span class="">This says: </span>*<span class="">“the object's </span>`:types`<span class=""> list includes a type from which </span>`Folder`<span class=""> is derived”</span>*<span class="">. It's the mirror image of </span>`:types INHERITS "Folder"`<span class=""> and is used when the relation points at the </span>*<span class="">parent</span>*<span class=""> rather than the child. If you need it in day-to-day work, use </span>`INHERITS`<span class=""> on the property side; the reversed form is mostly emitted by tools.</span>

### <span class="">7.3 Relation metadata — </span>`RELATES TO … TOWARDS ID`<span class=""> / </span>`FROM ID`

<span class="">Relations can carry per-target metadata — a set of named key-value paris (such as `inheritPermissions: ['read']`</span><span class="">, </span>`cascadeDestroy: false`, etc<span class="">)</span><span class="">. </span>`RELATES TO`<span class=""> filters by this metadata.</span>

<span class="">Syntax:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-39"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
RELATES TO ID "<target-id>"
  TOWARDS ID "inheritPermissions"
    NOT "read", "cascadeDestroy" TRUE
```

- `RELATES TO ID "…"`<span class=""> — the object has a relation pointing at </span>`<target-id>`<span class="">.</span>
- `TOWARDS ID "<relation-name>"`<span class=""> — restrict to a specific relation property (e.g. </span>`inheritPermissions`<span class="">). You can also write `FROM ID "…"` to look at the incoming relation direction.</span>
- <span class="">Then a comma-separated list of values.</span>

`ALLOW EMPTY`<span class=""> may be added to an entry; it relates to empty relations. Its precise meaning is internal, and you don't need to know it in order to write or read the query — ask your administrator if you need the exact semantics.</span>

### <span class="">7.4 </span>`CONTAINS`

`CONTAINS`<span class=""> filters objects based on a sub-expression over one of their related objects, </span>**<span class="">without multiplying results</span>**<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-41"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:members CONTAINS [:role "Owner" AND :active TRUE]]
```

<span class="">This returns the original objects whose </span>`:members`<span class=""> list contains at least one object matching the sub-expression. Unlike a </span>`JOIN`<span class="">, it does not duplicate the result row for each match — it just says </span>*<span class="">"has a match"</span>*<span class="">.</span>

### <span class="">7.5 </span>`PERMITTED`

`PERMITTED`<span class=""> adds extra rights-based filtering on top of the normal “only what the user can read” behaviour:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-42"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
PERMITTED write, delete FALSE, read:x TRUE ;
```

- <span class="">Each comma-separated item names a right (or a </span>`right:subright`<span class="">, e.g. </span>`read:x`<span class="">).</span>
- <span class="">Add </span>`FALSE`<span class=""> after a right to </span>*<span class="">exclude</span>*<span class=""> objects where that right is granted; add </span>`TRUE`<span class=""> (or nothing) to </span>*<span class="">require</span>*<span class=""> it.</span>
- <span class="">A </span>`PERMITTED`<span class=""> clause </span>**<span class="">must end with a semicolon</span>** `;`<span class="">.</span>

<span class="">Results always contain only objects the user may read; </span>`PERMITTED`<span class=""> narrows the set further.</span>

### <span class="">7.6 </span>`IN PROVIDERS`

<span class="">Search only within specified providers:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-43"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] IN PROVIDERS ID "1@0", ID "2@0"
```

<span class="">Provider IDs are the number after the </span>`@`<span class=""> in an object ID. A list of active providers is available from the system and in the UI, so users can copy the ID of a provider when they need to.</span>

### <span class="">7.7 Empty joins — </span>`ALLOW EMPTY`<span class=""> and </span>`NOT JOIN`

- `JOIN ALLOW EMPTY :name [...]`<span class=""> — the outer-join variant: keep the original object even when the joined expression matches nothing. On those rows the joined properties will be absent.</span>
- `NOT JOIN :name [...]`<span class=""> — the </span>*<span class="">anti-join</span>*<span class="">: keep only original objects that </span>**<span class="">have no</span>**<span class=""> matching joined object.</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-44"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] NOT JOIN :comments [:spam TRUE]
```

<span class="">This returns active objects whose comments contain no spam — i.e., objects </span>*<span class="">without</span>*<span class=""> a matching comment.</span>

### <span class="">7.8 Advanced map expressions</span>

<span class="">Maps support much more than simple arithmetic. The full grammar is:</span>

<div class="ds-scroll-area ds-scroll-area--show-on-focus-within ds-scroll-area--enabled _1210dd7 c03cafe9" id="bkmrk-form-meaning-if-%3Ccon"><div class="ds-scroll-area__gutters"><div class="ds-scroll-area__horizontal-gutter">  
</div><div class="ds-scroll-area__vertical-gutter">  
</div></div><table><thead><tr><th><span class="">Form</span></th><th><span class="">Meaning</span></th></tr></thead><tbody><tr><td>`IF <cond> THEN <expr> ELSE <expr>`</td><td><span class="">Ternary</span></td></tr><tr><td>`<expr> + <expr>`<span class="">, </span>`<expr> - <expr>`</td><td><span class="">Addition / subtraction</span></td></tr><tr><td>`<expr> * <expr>`<span class="">, </span>`<expr> / <expr>`</td><td><span class="">Multiplication / division</span></td></tr><tr><td>`( <expr> )`</td><td><span class="">Grouping</span></td></tr><tr><td>`CURRENCY_CONVERT( <prop> AS <CUR> )`</td><td><span class="">Currency conversion</span></td></tr><tr><td>`sum(<expr>)`<span class="">, </span>`count(<expr>)`<span class="">, </span>`average(<expr>)`<span class="">, </span>`median(<expr>)`<span class="">, </span>`min(<expr>)`<span class="">, </span>`max(<expr>)`<span class="">, </span>`first(<expr>)`<span class="">, </span>`concat(<expr>)`</td><td><span class="">Aggregation over joined objects within a </span>`MAP`</td></tr></tbody></table>

</div><span class="">Conditions inside </span>`IF`<span class=""> are the same comparisons as in filters (</span>`=`<span class="">, </span>`!=`<span class="">, </span>`<`<span class="">, </span>`>`<span class="">, </span>`<=`<span class="">, </span>`>=`<span class="">), but written without the surrounding </span>`[...]`<span class="">:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-45"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
MAP :tier AS ( IF :spend > 1000 THEN "gold" ELSE "standard" )
MAP :z     AS ( :a + :b * :c - :d / :e )
MAP :avg   AS ( average(:child:duration) )
```

### <span class="">7.9 Reversed inheritance</span>

<span class="">Already covered in §7.2 — </span>`"Type" INHERITS :property`<span class=""> is the mirror of </span>`:property INHERITS "Type"`<span class="">.</span>

---

## <span class="">8. Precedence and putting it together</span>

<span class="">When multiple operators appear in the same filter, they are evaluated in this order (highest first):</span>

1. **<span class="">Brackets </span>`( )`**<span class=""> — always win.</span>
2. **`NOT`**
3. **`AND`**<span class=""> (explicit or implicit — adjacent conditions)</span>
4. **`OR`**

<span class="">Examples:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-46"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:a = 1] OR [:b = 2] AND [:c = 3]
```

<span class="">means:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-47"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:a = 1] OR ([:b = 2] AND [:c = 3])
```

<span class="">Use brackets to change the meaning:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-text-48"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
([:a = 1] OR [:b = 2]) AND [:c = 3]
```

<span class="">Clauses (</span>`SORT BY`<span class="">, </span>`LIMIT`<span class="">, </span>`JOIN`<span class="">, </span>`GROUP BY`<span class="">, </span>`AGGREGATE`<span class="">, </span>`MAP`<span class="">, </span>`FLATTEN`<span class="">, </span>`RETURN`<span class="">, </span>`PERMITTED`<span class="">, </span>`IN PROVIDERS`<span class="">) are applied </span>**<span class="">in the order they appear</span>**<span class=""> in the query.</span>

---

## <span class="">9. Cookbook</span>

<span class="">Here are ready-to-copy examples covering the most common patterns.</span>

**<span class="">Everything that mentions “laptop”:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-49"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
laptop
```

**<span class="">Active products under 100:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-50"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] [:price < 100]
```

**<span class="">Documents whose owner is a manager (a joined property):</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-51"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:types INHERITS "Document"]
  JOIN :owner [:role "Manager"]
  SORT BY :owner:name ASC
```

**<span class="">My tasks that are due this week:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-52"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:assignee ME] [:dueAt <= THIS WEEK + 1 WEEK] SORT BY :dueAt ASC
```

**<span class="">Distinct status counts:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-53"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price >= 0] GROUP BY :status AGGREGATE count(:id) AS :n
```

**<span class="">Monthly totals for this year:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-54"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:createdAt >= THIS YEAR]
  GROUP BY :createdAt MONTHLY
  AGGREGATE sum(:total) AS :sum
  AGGREGATE count(:id) AS :count
  SORT BY :createdAt ASC
```

**<span class="">A label based on a status:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-55"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status] MAP :label AS ( IF :status "Active" THEN "Live" ELSE "Draft" )
```

**<span class="">Total and average price, rounded to currency:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-56"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:price >= 0]
  MAP :priceUSD AS ( CURRENCY_CONVERT( :price AS USD ) )
  AGGREGATE sum(:priceUSD)   AS :total
  AGGREGATE average(:priceUSD) AS :average
```

**<span class="">Comments on active documents, newest first:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-57"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] RETURN :comments SORT BY :createdAt DESC LIMIT 10
```

**<span class="">Documents without any review:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-58"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:types INHERITS "Document"] NOT JOIN :reviews [:]
```

**<span class="">Semantic search for “reset my password”:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-59"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
SEM "reset my password"
```

**<span class="">Keyword search for a specific term:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-60"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
KEY "invoice"
```

**<span class="">Only returns objects the user can write to (fine-grained rights):</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-61"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:types INHERITS "Document"] PERMITTED write ;
```

**<span class="">Search only one provider:</span>**

<div class="md-code-block md-code-block-dark" id="bkmrk-text-62"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">text</span></div></div></div></div></div>```
[:status "Active"] IN PROVIDERS ID "1@0"
```

---

## <span class="">10. Summary cheat-sheet</span>

<div class="ds-scroll-area ds-scroll-area--show-on-focus-within ds-scroll-area--enabled _1210dd7 c03cafe9" id="bkmrk-want-to%E2%80%A6-use-search-"><div class="ds-scroll-area__gutters"><div class="ds-scroll-area__horizontal-gutter">  
</div><div class="ds-scroll-area__vertical-gutter">  
</div></div><table><thead><tr><th><span class="">Want to…</span></th><th><span class="">Use</span></th></tr></thead><tbody><tr><td><span class="">Search any text</span></td><td>`word`<span class=""> / </span>`"phrase"`<span class=""> / </span>`word*`<span class=""> / </span>`w?rd`</td></tr><tr><td><span class="">Search one property's text</span></td><td>`[:name word]`</td></tr><tr><td><span class="">Force keyword vs semantic</span></td><td>`KEY word`<span class=""> / </span>`SEM "some meaning"`</td></tr><tr><td><span class="">Check existence</span></td><td>`[:property]`</td></tr><tr><td><span class="">Compare</span></td><td>`[:prop = value]`<span class="">, </span>`!=`<span class="">, </span>`<`<span class="">, </span>`>`<span class="">, </span>`<=`<span class="">, </span>`>=`</td></tr><tr><td><span class="">Compare two properties</span></td><td>`[:a < :b]`</td></tr><tr><td><span class="">Compare many values</span></td><td>`[:x ( a, b, c )]`<span class=""> / </span>`[:x ( a TO b )]`</td></tr><tr><td><span class="">Match a type or subtype</span></td><td>`[:prop INHERITS "Type" / ID "…@N"]`</td></tr><tr><td><span class="">Reference the current user</span></td><td>`[:prop ME]`<span class=""> / </span>`[:prop MY GROUPS]`</td></tr><tr><td><span class="">Count array items</span></td><td>`[:prop LENGTH > 5]`</td></tr><tr><td><span class="">Date shortcuts</span></td><td>`TODAY`<span class="">, </span>`NOW - 1 MONTH`<span class="">, </span>`2024-03-15`</td></tr><tr><td><span class="">Combine</span></td><td>`AND`<span class=""> (or adjacent), </span>`OR`<span class="">, </span>`NOT`<span class="">, </span>`( )`</td></tr><tr><td><span class="">Sort / limit</span></td><td>`SORT BY :a ASC, :b DESC`<span class=""> / </span>`LIMIT n`</td></tr><tr><td><span class="">Join / anti-join / outer</span></td><td>`JOIN :n […]`<span class=""> / </span>`NOT JOIN :n […]`<span class=""> / </span>`JOIN ALLOW EMPTY :n […]`</td></tr><tr><td><span class="">Flatten an array</span></td><td>`FLATTEN :arr`</td></tr><tr><td><span class="">Group and summarise</span></td><td>`GROUP BY :x AGGREGATE sum(:y) AS :total`</td></tr><tr><td><span class="">Compute a new field</span></td><td>`MAP :z AS ( :x + :y )`</td></tr><tr><td><span class="">Convert currency</span></td><td>`MAP :z AS ( CURRENCY_CONVERT( :amount AS USD ) )`</td></tr><tr><td><span class="">Return related objects</span></td><td>`RETURN :attachments`</td></tr><tr><td><span class="">Rights-based filtering</span></td><td>`PERMITTED write ;`</td></tr><tr><td><span class="">Target specific providers</span></td><td>`IN PROVIDERS ID "1@0", ID "2@0"`</td></tr><tr><td><span class="">Check specific ID relations</span></td><td>`RELATES TO ID "…" TOWARDS ID "…" "read" TRUE`</td></tr><tr><td><span class="">Set-of-overlap on relations</span></td><td>`[:a ~= :b]`</td></tr></tbody></table>

</div>---