CodamAIDocs
Topicdone

AND/OR groups

How type, filter and group form a filter tree, why the default is OR and how the tree becomes a condition.

Variants
one group ANDone group OR (default)nested groupsOR group with filter and groupforgotten typeempty group

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… ]
}
KeyMeaning
typeAND: all entries must match. OR: at least one.
filtersingle conditions, see All filter operators
groupsubgroups, 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 writeCDMS 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

Groups in all their forms

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”

Filter city = Köln and active = true, without and with type
typeCustomer from Köln, activeCustomer from Köln, inactiveCustomer from Bonn, activeResult
ANDyesnono1 match – as intended
missing (= OR)yesyesyes3 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.

Sources in the code and the knowledge base
  • CDMS/cdms-persistence-database – DatabaseConditionBuilder.addWhere
  • CDMS/cdms-commons – ListSearchLogic (type = OR as default)
  • CDMS/cdms-system-layer – AbstractLayer.buildSearchRoot
Search