CodamAIDocs
Topicdone

Search patterns with LIKE: % and _

How the placeholders % and _ work in a LIKE search, why CDMS does not add % automatically, that there is no escaping, and what upper and lower case depend on.

Variants
exact value without %starts with (abc%)ends with (%abc)contains (%abc%)exactly one character (_)% or _ as a real characterupper/lower case depends on the databaseLIKE is the default operatorLIKE on a non-text field

What this is about

In a search (POST /query) you write filters like this one:

{ "key": "name", "value": "Muster%", "param": "LIKE" }

LIKE compares a text with a search pattern. The pattern has two placeholders:

Characterstands forExamplematches
%any number of characters, including noneMus%Mus, Muster, Muster GmbH
_exactly one characterM_ierMaier, Meier, but not Mayer

Do not confuse them with the wildcards + and * in the field selection: those decide which fields come back. % and _ decide which rows are found.

Which pattern matches what?

Example data: five customers with these names.

Five patterns against five names
Muster GmbHMustermannAlt-Muster AGmusterMeisterPattern (value)
nononodepends on DBnoMuster – without placeholders: exactly this text only
yesyesnodepends on DBnoMuster% – starts with “Muster”
nononodepends on DBno%Muster – ends with “Muster”
yesyesyesdepends on DBno%Muster% – contains “Muster”
nonononoyesM__ster – M, any two characters, then “ster”

“depends on DB” means: whether muster (lower case) is matched by Muster (upper case) is decided by the database, not by CDMS. More on this further below.

What happens along the way

  1. 1
    User→Client
    types “Muster” into the search box
  2. 2
    Client
    builds the filter. This is where it is decided whether Muster, Muster% or %Muster% is sent
  3. 3
    Client→CDMS
    sends { "key": "name", "value": "%Muster%", "param": "LIKE" }
  4. 4
    CDMS
    translates the filter 1:1 into name LIKE '%Muster%'. No rewriting, no additions, no escaping
  5. 5
    CDMS→Database
    runs the query, together with the filters that always run along
  6. 6
    Database
    compares by its own rules (collation), which includes with or without case sensitivity
    Result: Matches come back as a list

All variants

LIKE in all its forms

When: A search box where the text may appear anywhere.

The client sends %text%. This is the usual form for a free search.

Result: Finds “Muster GmbH”, “Mustermann” and “Alt-Muster AG”.

When: Autocomplete, number ranges, prefixes.

The client sends text%. On large tables this form is usually much faster than “contains”, because the database can use an index.

Result: Finds “Muster GmbH” and “Mustermann”.

When: File extensions, domains in email addresses.

The client sends %text, for example %@codamic.com.

Result: All values with exactly this ending.

When: Spelling variants with a fixed length, for example Meier/Maier.

_ stands for exactly one character. M_ier matches Maier and Meier, M__er matches all five-letter names with M at the start and er at the end.

Result: Only values with exactly the matching length.

When: usually by mistake.

The client sends text without %. This is an equality check, except that, depending on the database, upper and lower case may be ignored.

Result: Only exactly “text”. If you mean equality, use EQ instead.

When: The text you are looking for itself contains a % or _, for example 10% or max_wert.

CDMS has no escaping. max_wert also finds maxXwert, because _ counts as a placeholder. You cannot search for a real % or _ with LIKE.

Result: Too many matches. Workaround: EQ for exact values, or filter again in the client.

When: The filter has no param.

LIKE is the default operator. So { "key": "name", "value": "Muster" } is a LIKE without placeholders, and therefore an exact comparison.

Result: Same behavior as “without placeholders”.

Upper and lower case

CDMS itself does nothing with the spelling. Whether muster and Muster are equal is decided by the collation of the database column:

MySQL 8 (production)
Default collation utf8mb4_0900_ai_ci
  • Upper and lower case are ignored
  • Accents are mostly ignored: “Muller” finds “Müller”
  • applies as long as the column was not created differently
H2 (tests)
  • Upper and lower case matter
  • so a test can have a different result than production

What LIKE is not meant for

  • Only for text fields. For numbers, dates, yes/no and IDs there are EQ, IN, BEFORE, AFTER and the other operators.
  • No regular expressions. [A-Z], .* or ^ have no special meaning.
  • No full-text search. If you search across several fields, combine several LIKE filters in an OR group.
Search across three fields
Request
"query": {
  "type": "OR",
  "filter": [
    { "key": "name",           "value": "%muster%", "param": "LIKE" },
    { "key": "email",          "value": "%muster%", "param": "LIKE" },
    { "key": "customerNumber", "value": "%muster%", "param": "LIKE" }
  ]
}
Response
Finds every customer where “muster” appears in the name,
email OR customer number.
Sources in the code and the knowledge base
  • CDMS/cdms-persistence-database – DatabaseConditionBuilder, case LIKE
  • CDMS/cdms-commons – ListSearchFilter (param = LIKE as default)
Search