---
key: rulebuilder.relative-date-macros
title: 2026-07-23 — Relative-date rule builder operators are a frontend-only mapping onto existing Lucene macros (#435)
status: active
since: '2026-07-23'
supersedes: none
superseded-by: none
rule: The visual rule builder's `inLast`/`notInLast` date operators compile to/parse from the pre-existing `CustomMultiFieldQueryParser` macros `released_inthelast`/`released_notinthelast` and `added_inthelast`/`added_notinthelast`, value form `"<n> day|week|month|year"`; there is no backend change.
signals: 'relative date operator, inLast, notInLast, inthelast macro, released_inthelast, added_inthelast, date unit picker, rule builder relative dates · paths: `web/src/builder/rules/dateMacro.ts`, `web/src/builder/rules/compile.ts`, `web/src/builder/rules/parse.ts`, `web/src/builder/rules/types.ts`, `web/src/builder/rules/validation.ts` · issues: #435, #176, #438'
mechanics: '`dateMacro.ts` (`compileRelative`/`parseRelative`, `OP_TO_SUFFIX`/`SUFFIX_TO_OP`, `RELATIVE_SYNTHETIC`); spa-conventions.md §12'
---

`released_inthelast`/`added_inthelast` (+ their `notinthelast` negations) already existed in
`CustomMultiFieldQueryParser` as free-text macro fields before the rule builder could reach them — they
were only usable by typing raw Lucene. #435 exposes them as first-class builder operators without touching
the parser: `release_date`/`added_date` gain `inLast`/`notInLast` alongside the existing `before`/`after`/
`between`, backed by a numeric-value input plus a `day|week|month|year` unit picker (`types.ts`'s `unit?:
DateUnit`). `dateMacro.ts` is the single seam — a small `field↔macro-prefix` table (`release_date↔released`,
`added_date↔added`) plus the `inLast/notInLast ↔ inthelast/notinthelast` suffix maps — that `compile.ts`
delegates to for these two fields and `parse.ts` recognizes via `RELATIVE_SYNTHETIC` before falling into the
generic field:value grammar. Validation (`ruleError` in `validation.ts`) requires the value to parse as a
positive integer; a non-numeric or non-positive value is a builder-side error, never sent to the server.

**Frontend-only because the macros are the query-string wire format, not a new query kind.** The compiled
query for `release_date inLast "7 day"` is literally `released_inthelast:"7 day"` — the same string a user
could type by hand — so nothing downstream (search index, SmartCollection storage, Auto-Tune) needs to know
the rule builder exists. This keeps `rulebuilder.relative-date-macros` symmetric with
`spa.smartcollection-rule-builder`'s "compile-only, no new stored AST" stance (#176): a relative-date rule is
just another point in the same closed grammar subset, proven by the same compile→parse round-trip discipline
(`dateMacro.test.ts`, and the property test in `roundtrip.test.ts`, #438) rather than a special case.
