When taxonomy dimensions are retired (e.g., a motor type is split into two, a trim is reclassified), users searching for old cat values get redirected to the current ones.
- Category (
cat): a combination of dimensions likema13gr100037mt1314(make=BMW, modelGroup=1 Series, motorType=M135) - Rule: maps a retired dimension value to its replacement(s). Example: motor type "M135" (id 1314) → "1er M" (id 903)
- Patch: what a rule produces — the new dimension values to apply
- Parse the input
catparameter - Find rules that match (all "from" dimensions present in the input)
- Remove retired dimensions from the input → base
- Sort patches (size ascending, then dimension priority sum descending) and apply them one by one:
- If the patch introduces NEW dimensions → merge into existing results
- If ALL patch dimensions ALREADY EXIST → split (create additional cat with those dims replaced)
- If SOME patch dimensions exist and some don't → drop + log warning (partial overlap edge case)
A motor type was renamed.
Rule: motorType 1314 (M135) → motorType 903 (1er M)
Input: ma13gr100037mt1314
BMW, 1 Series, M135
Base: ma13gr100037 (remove retired: mt1314)
Patch: mt903
Apply mt903 → no motorType in base → merge
Result: ma13gr100037mt903
BMW, 1 Series, 1er M
A motor type was renamed AND a trim was renamed (independently).
Rules:
- trim 1 → motorType 2
- motorType 3 → trim 4
Input: tr1mt3
Base: (empty — both dims retired)
Patches (size 1): mt2 (priority=2), tr4 (priority=1)
Sorted: mt2, tr4
Apply mt2 → no motorType → merge → [mt2]
Apply tr4 → no trim → merge → [mt2tr4]
Result: mt2tr4
A trim "6.2L AMG" was reclassified: "6.2L" is actually the motor type, "AMG" is the trim.
Rule: trim 16420 (6.2L AMG) → motorType 12610 (6.2L) + trim 479759 (AMG)
Input: ma47gr100056tr16420
Mercedes C-Class, trim "6.2L AMG"
Base: ma47gr100056 (remove retired: tr16420)
Patch: mt12610tr479759
Apply mt12610tr479759 → no motorType or trim in base → merge
Result: ma47gr100056mt12610tr479759
Mercedes C-Class, motor "6.2L", trim "AMG"
Two independent rules both produce the same dimension.
Rules:
- trim 1 → variant 3
- motorType 2 → variant 4
Input: tr1mt2
Base: (empty)
Patches (size 1): va3 (priority=3), va4 (priority=3)
Same dims → order doesn't matter
Apply va3 → no variant → merge → [va3]
Apply va4 → has variant → split → [va3, va4]
Result: va3, va4
A generation "F20" was split into "F20 Pre-facelift" and "F20 LCI".
Rules:
- generation 1 (F20) → generation 2 (F20 Pre-facelift)
- generation 1 (F20) → generation 3 (F20 LCI)
Input: ma13gr100037ge1
BMW, 1 Series, F20
Base: ma13gr100037 (remove retired: ge1)
Patches: ge2 (priority=4), ge3 (priority=4)
Apply ge2 → no generation in base → merge → [ma13gr100037ge2]
Apply ge3 → has generation → split → [ma13gr100037ge2, ma13gr100037ge3]
Result: ma13gr100037ge2, ma13gr100037ge3
BMW 1 Series F20 Pre-facelift, BMW 1 Series F20 LCI
Generation "F20" split into 2, AND motor "M135i" split into 2 variants.
Rules:
- generation 1 → generation 2 (F20 Pre-facelift)
- generation 1 → generation 3 (F20 LCI)
- motorType 4 → motorType 5 (M135i xDrive)
- motorType 4 → motorType 6 (M135i sDrive)
Input: ma13gr100037ge1mt4
BMW, 1 Series, F20, M135i
Base: ma13gr100037 (remove retired: ge1, mt4)
Patches (all size 1): ge2(sum=4), ge3(sum=4), mt5(sum=2), mt6(sum=2)
Sorted desc by sum: ge2, ge3, mt5, mt6
Apply ge2 → merge → [ma13gr100037ge2]
Apply ge3 → split (has ge) → [ma13gr100037ge2, ma13gr100037ge3]
Apply mt5 → merge (no mt) → [ma13gr100037ge2mt5, ma13gr100037ge3mt5]
Apply mt6 → split (has mt) → [ma13gr100037ge2mt5, ma13gr100037ge2mt6,
ma13gr100037ge3mt5, ma13gr100037ge3mt6]
Result: 4 cats — all combinations of the two splits
Model group "Prius Prime" is retired. Different trims map to different outputs, but variant+trim stay paired.
Rules:
- variant 1 → variant 2, trim 3
- variant 1 → variant 7, trim 6
Input: ma1gr2va1
Base: ma1gr2 (remove retired: va1)
Patches (both size 2): va2tr3 (sum=3+1=4), va7tr6 (sum=3+1=4)
Same dims → order doesn't matter
Apply va2tr3 → no variant or trim → merge → [ma1gr2va2tr3]
Apply va7tr6 → has variant AND trim → split → [ma1gr2va2tr3, ma1gr2va7tr6]
Result: ma1gr2va2tr3, ma1gr2va7tr6
(values stay paired — NOT a cartesian product)
A variant was retired with no replacement — users should see the broader model group.
Rule: variant 3314 (911 Carrera T Cabriolet) → (nothing)
Input: ma65gr75292va3314
Porsche, 911, 911 Carrera T Cabriolet
Base: ma65gr75292 (remove retired: va3314)
Patch: (empty — no "to" dimensions)
Nothing to apply.
Result: ma65gr75292
Porsche, 911
An input has a generation split (1→2), a motor rename (1→1), and a variant+trim rename (1→1).
Rules:
- trim 1 → motorType 2
- generation 8 → generation 9
- generation 8 → generation 10
- variant 3 → variant 4, trim 5
Input: ma6gr7tr1va3ge8
Base: ma6gr7 (remove retired: tr1, va3, ge8)
Patches sorted by size ASC, then priority sum DESC:
ge9 (size 1, priority=4)
ge10 (size 1, priority=4)
mt2 (size 1, priority=2)
va4tr5 (size 2, priority=4)
Apply ge9 → merge → [ma6gr7ge9]
Apply ge10 → split (has ge) → [ma6gr7ge9, ma6gr7ge10]
Apply mt2 → merge (no mt) → [ma6gr7ge9mt2, ma6gr7ge10mt2]
Apply va4tr5 → merge (no va/tr) → [ma6gr7ge9mt2va4tr5, ma6gr7ge10mt2va4tr5]
Result: ma6gr7ge9mt2va4tr5, ma6gr7ge10mt2va4tr5
Two rules produce patches that partially overlap in dimensions. This is an edge case that shouldn't happen in well-defined rules, but is handled gracefully.
Rules:
- variant 1 → variant 2, trim 3
- motorType 4 → motorType 5, trim 6
Input: va1mt4
Base: (empty — both dims retired)
Patches (size 2): va2tr3 (sum=3+1=4), mt5tr6 (sum=2+1=3)
Sorted desc by sum: va2tr3, mt5tr6
Apply va2tr3 → no variant, no trim → merge → [va2tr3]
Apply mt5tr6 → has trim? YES, has motorType? NO → PARTIAL OVERLAP → drop + warn
Result: va2tr3 (with warning logged about mt5tr6 being dropped)
This case indicates the rules may need manual review — two independent changes both setting "trim" creates ambiguity.