What this is about
A search rarely has only one condition. “Customers from Köln and with an open order”, “name contains Muster or email contains Muster”. For this, query is a tree of groups:
{
"type": "AND",
"filter": [ …conditions… ],
"group": [ …subgroups, each again with type, filter, group… ]
}
| Key | Meaning |
|---|---|
type | AND: all entries must match. OR: at least one. |
filter | single conditions, see All filter operators |
group | subgroups, nested as deep as you like |
From tree to formula
flowchart TB
R["AND"] --> F1["city = Köln"]
R --> G["OR"]
G --> F2["name LIKE %muster%"]
G --> F3["email LIKE %muster%"]
{
"type": "AND",
"filter": [ { "key": "city", "value": "Köln", "param": "EQ" } ],
"group": [
{
"type": "OR",
"filter": [
{ "key": "name", "value": "%muster%", "param": "LIKE" },
{ "key": "email", "value": "%muster%", "param": "LIKE" }
]
}
]
}
becomes
city = 'Köln' AND (name LIKE '%muster%' OR email LIKE '%muster%')
How CDMS translates a group
Every group becomes one condition. CDMS combines all entries of the group with the type of the group: every filter from filter and every subgroup from group. A subgroup counts as one entry and stands in parentheses.
flowchart LR
G["Group with type T"] --> A["filter A"]
G --> B["filter B"]
G --> U["Subgroup U<br/>(own type)"]
A --> F["A T B T (U)"]
B --> F
U --> F
So it makes no difference whether a condition is in filter or in a subgroup of its own in group. Both are entries of equal rank in the group:
| You write | CDMS evaluates |
|---|---|
{ "type": "OR", "filter": [A, B] } | A OR B |
{ "type": "OR", "filter": [A], "group": [ { "type": "AND", "filter": [B, C] } ] } | A OR (B AND C) |
{ "type": "OR", "group": [ { "type": "AND", "filter": [A] }, { "type": "AND", "filter": [B, C] } ] } | A OR (B AND C), the same as above |
{ "type": "AND", "filter": [A], "group": [ { "type": "OR", "filter": [B, C] } ] } | A AND (B OR C) |
CDMS leaves out an empty subgroup ("filter": [], "group": []). It restricts nothing, and it does not turn an OR group into “everything” either.
All variants
When: All conditions must match.
{ "type": "AND", "filter": [ { "key": "city", "value": "Köln", "param": "EQ" }, { "key": "active", "value": "true", "param": "EQ" } ] }
Result: Active customers from Köln.
When: One condition is enough, e.g. a search across several fields.
{ "type": "OR", "filter": [ …name…, …email… ] }
Result: Customers whose name or email matches.
When: AND and OR mixed.
The outer group is AND with the fixed conditions in filter, the alternatives go as an OR subgroup into group. The depth is not limited.
Result: city = Köln AND (name … OR email …)
When: You write { "type": "OR", "filter": [A], "group": [G] }.
A and G are alternatives of equal rank. If G is an AND group, G must match as a whole.
Result: A OR (G): all objects that match A or G.
When: You write { "filter": [ …city…, …active… ] } without type.
The group is then OR. “Active customers from Köln” becomes “everyone from Köln and everyone who is active”.
Result: More matches than expected.
When: "query": { "type": "AND", "filter": [], "group": [] } or "query": null
A group without entries restricts nothing.
Result: All visible objects.
The trap “too many matches”
| type | Customer from Köln, active | Customer from Köln, inactive | Customer from Bonn, active | Result |
|---|---|---|---|---|
| AND | yes | no | no | 1 match – as intended |
| missing (= OR) | yes | yes | yes | 3 matches – too many |
Your group and the security filters
Your query is never the whole condition. CDMS puts an AND root above it and adds the filters that always run along. If your query is an AND group, its entries are taken over directly into this root. If it is an OR group, it becomes a subgroup as a whole:
flowchart TB
W["AND (root from CDMS)"] --> C["your query<br/>(for OR as its own subgroup)"]
W --> S1["own data: _userId = you"]
W --> S2["attribute filters"]
W --> S3["custom required filters"]
So your OR can never get around the security filters: it always stands next to them, never above them.