Skip to content

Search Grammar

Type like you think. The search understands.

JinDesk search bar accepts a progressive grammar — beginners type words, experts type expressions, both get results.


The simplest case: type any words. Each word is matched as a word-start — the term must appear at the start of a word in one of the visible columns (a word ends at a space, underscore, hyphen, or dot). Multiple words become AND filters. Double-quote an exact phrase.

paracetamol → any word starts with "paracetamol"
hip replacement → AND of two word-start matches
"Scalpel Blade" → the phrase at a word boundary

If you want “contains anywhere” (not just word-start), use ~ — see Level 3.

Comparisons on numeric and date columns. Ranges with ...

>1000 → any numeric column > 1000
<50 → any numeric column < 50
100..500 → any numeric column between 100 and 500
2025-01..2025-06 → any date column in this range
>=high → ordinal: low < medium < high

Ranges also work for dates at month granularity (2025-01..2025-06), day granularity (2025-04-01..2025-04-30), or mixed.

Three prefixes control how a term matches:

PrefixMeaning
(none)word-starts-with (default)
=exact match
~regex — also how you do “contains anywhere”

Applies both to bare terms and to field-qualified ones (next level):

ortho → any word starts with "ortho"
=ortho → exactly "ortho"
~ortho → regex / contains "ortho" anywhere
~^ortho → regex anchored to column start

Scope a match to a single column with !field:value. The three prefixes above apply here too:

!amount:>1000 → amount column > 1000
!date:2025-01..2025-06 → date in range
!material_id:630664 → word-start on material_id
!material_id:=630664 → exact match
!name:~hip|knee → regex on name column
!status:null → IS NULL
-!status:null → IS NOT NULL (negation; there is no separate "not_null" syntax)
!is_active:true → boolean

Field filters compose naturally — use them for one column, leave plain words for the rest.

Combine terms with OR, exclude with -, group with parentheses.

hip OR knee → either term matches
hip | knee → shorthand for OR
-consumable → NOT: exclude rows matching "consumable"
!cat:ortho -!s:pending → ortho category, NOT pending status
hip (ortho | cardiac) → hip AND (ortho OR cardiac)

Whitespace between terms is AND. OR / | between terms is OR. - in front of a term (or -!field:value) negates it.

Natural dates that expand to real date ranges against any date column. this-* tokens use the full calendar period (not “up to today”) so the result stays the same whether you ask on the 1st or the 30th of the month.

yesterday → yesterday's date (one day)
today → today (one day)
tomorrow → tomorrow (one day)
last-7d → last 7 days, ending today
last-30d → last 30 days, ending today
next-7d → next 7 days, starting today
next-30d → next 30 days, starting today
last-Nd / next-Nd → any N works
last-week → full previous week
this-week → full current week
next-week → full following week
last-month → full previous calendar month
this-month → full current calendar month
next-month → full following calendar month
last-year → full previous calendar year
this-year → full current calendar year
next-year → full following calendar year
last-week-3 → the week that was 3 weeks ago
next-week-2 → the week 2 weeks from now
last-month-12 → the month 12 months ago
next-year-5 → the year 5 years from now

The stepped forms (last-week-N, last-month-N, last-year-N and their next- counterparts) give you any offset without reaching for literal dates. Three handy identities: last-week-1last-week; last-week-0this-week; a “trailing 12 months” window is simply last-month-12..last-month-1.

The next-* family lands squarely on future dates — useful for columns that store intent (material expiration, contract end, scheduled admission, warranty boundary).

Week boundaries follow display.search.week_start (default: Monday, per ISO 8601 / DACH convention). Set it to sunday in config.yml if you want calendar-week semantics instead.

Smart date tokens also compose inside ranges — either or both sides of .. can be a token:

last-year..this-year → start of last year to end of this year
today..next-week → today through end of next week
this-week..today → Monday of this week through today
last-month..2026-06-30 → full previous month through that literal date

The left side of the range uses the token’s start; the right side uses its end. Everything else the range accepts (ISO dates, pure numbers) still works on either side.

Range bounds are order-agnostic. 500..100 means the same as 100..500; next-year..last-year means the same as last-year..next-year. The parser / SQL layer always binds the interval as (min, max), so misordered bounds never silently return zero rows.

Period + day truncation (the useful special case). When a period token (this-month, this-year, etc.) is combined with a single-day token (today), the range uses the period’s inner edge, giving you the common month-to-date / year-to-date / week-to-date idioms:

this-month..today → 1st of this month through today (MTD)
this-year..today → Jan 1 through today (YTD)
this-week..today → Monday of this week through today (WTD)
today..this-week → today through Sunday of this week

Under the hood: the left bound contributes its .start, the right bound contributes its .end. If that produces a reversed pair (e.g. next-year..last-year), the parser falls back to the union of both spans — so order-agnostic symmetry still holds for reversed inputs.

Open ranges. Leave one side blank to make the range unbounded on that side — X.. means “from X onwards,” ..Y means “up to and including Y.” Works for numbers, ISO dates, and smart date tokens:

100.. → any numeric column >= 100
..500 → any numeric column <= 500
today.. → from today onwards (today or later)
..next-month → up to the end of next month
last-year.. → from Jan 1 of last year onwards
!expires:today..next-month → anything expiring between now and end of next month
!shipped:..yesterday → everything shipped before today

Open ranges are often what you actually want for expiration, renewal, or shipment-date filters. last-year..this-year gives you a fixed 2-year window; last-year.. keeps rolling forward forever as time passes.


  1. Whitespace splits terms (AND by default).
  2. Quoted strings are single terms: "hip replacement".
  3. OR or | between terms creates an OR group.
  4. - prefix negates: -term, -!field:value.
  5. !field:value is a field filter.
  6. >, <, >=, <= before a number = comparison.
  7. N..M = range (numeric or date).
  8. Smart tokens (today, this-month, last-7d) expand to date ranges.
  9. Parentheses group OR expressions: (a | b) c.
  10. Backslash escapes let you embed spaces, quotes, or parens in an unquoted value: !name:hip\ replacement, !note:\"quoted\". \d, \., \[ etc. pass through unchanged so regex patterns work.

Every search bar has an ⓘ button on the right. Click it for a compact quick-reference of the grammar — Basic / Operators / Field filters / Numbers / Logic / Dates — with a single literal example per row. Press ESC or click outside to close.

The URL updates when you press Enter or when the input loses focus. Typing itself is purely local — no re-render mid-keystroke.


Filter 68,000 cases down to what you care about

Section titled “Filter 68,000 cases down to what you care about”

“High-severity materials over CHF 1,000 from last year”

!severity:=high !price:>1000 !date:2025-01..2025-12

Three field filters composed with implicit AND. One input, one query.

Find all ortho or cardiac cases that are not pending

Section titled “Find all ortho or cardiac cases that are not pending”
!cat:=ortho | !cat:=cardiac -!status:=pending

OR at the top level, then a negated exact field filter.

CHF >10000 last-30d

Shorthand + smart token. Works in finding views where CHF maps to money_at_risk.

=635465

Exact match across all visible columns — any column whose value equals 635465 matches.

Per tenant, in config.yml:

display:
search:
case_sensitive: false # default
word_separators: " _-." # chars that start a new "word"
week_start: monday # `monday` (default) or `sunday`

Tighten the separator set for stricter word-start matching; widen it to find substrings inside more identifier shapes. week_start changes what last-week / this-week / next-week consider a “week.”


jazzisnow jinflow is a jazzisnow product
v0.64.7 · built 2026-09-20 19:48 UTC