--- title: Rules and effects description: Select the products a rule acts on, choose between pin, ban, boost and bury, and decide whether the effect holds at full strength or fades. slug: merchandising/business-rules/rules-and-effects docKind: guide hub: luigisbox-ai --- Each rule pairs a `record_match` — which products it applies to — with one `effect`. ```json { "name": "Bury clearance stock", "record_match": { "operator": "and", "conditions": [ { "field": "clearance", "operator": "eq", "value": true } ] }, "effect": { "type": "bury", "strength_pct": 0.6, "mode": "hard" } } ``` A rule's `name` is required and is for you: it is what someone reads in six months when they find results they cannot explain. :::hub[Config → Business Rules → Rules & Effects]{path="/business-rules"} Builds the same pairing — product selection on one side, one of pin, ban, boost or bury with its strength and mode on the other. ::: ## The four effects | Effect | What it does | Fields | |---|---|---| | `pin` | Place the product at a fixed position | `position` (1-indexed), `mode`, `decay_window` | | `ban` | Remove the product from results entirely | — | | `boost` | Raise the product's ranking | `strength_pct` (0–1), `mode`, `decay_window` | | `bury` | Lower the product's ranking | `strength_pct` (0–1), `mode`, `decay_window` | They divide into two kinds: - **`pin` and `ban` are absolute.** A pin puts a product at a position; a ban removes it. Both are decisions about the output rather than about how strongly a product scores. - **`boost` and `bury` are relative.** They change how strongly a product scores and let the results re-sort. Both are decisions about tendency. ### Pin `position` is 1-indexed: `1` is the top slot. The product is inserted there and everything below shifts down — nothing is dropped. A pin naming **one** product places that product. A pin whose selector names *several* products — a list of IDs, or a criterion like `brand eq "northwear"` — places whichever of them ranks highest, and several such pins fill their positions with distinct products. So "pin our best Northwear product to slot 1" is one rule, not a lookup you have to do yourself. A pinned product does not have to match the query. A campaign triggered by the query "wine" can pin a box of chocolates for Valentine's Day: the product is fetched and placed even though the search would never return it. A pin naming **one** product has nothing to place when that product does not exist or a ban removes it. A pin selecting by criteria, or by a list of IDs, moves down its matches instead: a ban takes its top match out of the running and the next one takes the slot, so it places nothing only once no match is left. A pin naming exactly one product also ignores the shopper's filters by default: it shows the product even when the active filters would exclude it. A pin selecting by criteria, or by a list of IDs, only places a product that passes those filters. Set `respect_user_filters` to `true` on the effect to turn that off, and the pin is filtered like any other product — it places nothing when the shopper's filters exclude what it names. Use it when a product must not be shown past a filter: an age-restricted or regulated item the shopper has filtered away should stay away. ```json { "type": "pin", "position": 1, "respect_user_filters": true } ``` ### Boost and bury `strength_pct` is a strength between 0 and 1, **not** a number of positions. How far a product actually moves depends on how tightly packed the results around it are, so a boost does not lift a product over results that match the query substantially better. Start around `0.3` for a nudge and `0.7` for a strong push, then look at the result. Boost and bury act on products the results already contain. A product that would not have been retrieved for this request is not boosted into it — a boost changes the order of an answer, it does not change what the answer is about. If you need a product to appear on a request it does not match, that is a pin. Boost and bury never cancel out: when both would apply to one product, one wins outright and the other is discarded. At equal specificity the bury wins — see [Resolving conflicts](/merchandising/business-rules/conflicts/). ### Hard and soft mode `pin`, `boost` and `bury` can fade. `mode: "hard"` (the default) applies the effect at full strength for the campaign's whole run. `mode: "soft"` fades it out — full strength at the start, tapering to nothing. A ban is always hard: there is no half of a product removed. ```json { "type": "boost", "strength_pct": 0.5, "mode": "soft", "decay_window": "P3D" } ``` What fading means depends on the effect. A soft boost or bury weakens its score modifier until it stops moving the product at all. A soft pin loosens its hold on the position: the product starts at the slot the pin names and drifts back toward where it would have ranked on its own, instead of dropping back there in one step when the campaign ends. ```json { "type": "pin", "position": 1, "mode": "soft", "decay_window": "P3D" } ``` `decay_window` is an ISO 8601 duration: `P3D` is three days, `PT12H` twelve hours. It is only valid with `mode: "soft"`, and must be positive. Leave it out and the fade lasts the whole campaign. The fade counts from `starts_at` and needs both `starts_at` and `ends_at`: a soft effect in a campaign without both bounds behaves like a hard one and never fades. Once the fade reaches zero the rule stops applying for the rest of the campaign. Soft mode is how you launch a new product hard and let it earn its own place, instead of ending a campaign with a cliff. ### Pin wins over boost on the same product Pins are placed after scores are adjusted, so a pin overrides any movement a boost gave the same product. A product boosted to position 2 and then pinned to 1 ends up at 1 — and a product boosted upward while another product is pinned above it lands one slot lower than the boost alone would suggest. Pin for a position, boost for a tendency. When both apply to one product, the pin decides. ## Selecting products `record_match` uses the same [condition syntax](/merchandising/business-rules/triggers/#condition-syntax) as a trigger, over catalog fields rather than `trigger.` fields. It selects **by identity** or **by criteria**, and the difference decides who wins a conflict: By identity — the most specific kind of rule: ```json { "field": "@id", "operator": "in", "value": ["product/sku-4410", "product/sku-4411"] } ``` By criteria — matches whatever fits, now and later: ```json { "field": "brand", "operator": "eq", "value": "brand/northwear" } ``` Prefer criteria. A rule on `clearance = true` keeps meaning the right thing as stock turns over; a list of IDs has to be maintained as stock changes. "Product" here means any catalog object the surface serves. The same selectors and effects apply to categories, brands or articles, and the ID carries the type: `category/shoes` works like `product/sku-1001`. For a `pin`, a list of several IDs counts as criteria, not identity: a pin claims one position, so several IDs mean "whichever of these ranks highest". Pin a single product when it must win a conflict on specificity. Operators follow the field's type: `gt`, `gte`, `lt`, `lte` and `range` need a numeric field; `eq`, `neq`, `in`, `not_in` and `exists` work on any field, as long as the literal matches that field's type. Break either rule — an operator the type does not support, or a literal of the wrong type — and the save fails with `422`. `matches` is not available in `record_match` at all. A selector is a little more permissive than a trigger, so read the [operator table](/merchandising/business-rules/triggers/#condition-syntax) as the trigger's set rather than this one's: membership works on a boolean here, where a trigger admits only `eq`, `neq` and `exists`. `{"field": "clearance", "operator": "in", "value": [true]}` is accepted in a selector and rejected in a trigger. The [missing-attribute rule](/discovery/filters/#missing-attributes) bites here more than anywhere: a rule matching `{"field": "@category", "operator": "neq", "value": "category/shoes"}` also fires on every product with no category at all. ### An empty selector For `ban`, `boost` and `bury`, a selector with no conditions is a scope-wide effect — blunt, but coherent, and occasionally what you want. For `pin` it is rejected: a pin places one product at one position, so a pin that names no product asks for a placement without saying what to place. ### Matching against the trigger A rule's `value` can reference the trigger instead of a literal — "products whose brand is the same as the anchor's brand": ```json { "field": "brand", "operator": "eq", "value": { "trigger_ref": "trigger.brand" } } ``` That is one rule that works for every brand, instead of one rule per brand. The operator has to match the reference's cardinality: a reference resolving to a single value takes `eq` or `neq`, one resolving to a list takes `in` or `not_in`. On a search request there is no anchor to read the attribute from, so a rule referencing `trigger.brand` matches no product there. For a list-valued trigger attribute, pick which elements to use with `path` and `select` — `first`, `last`, `all`, `first_n`, `last_n`, `at_index`, `all_but_first`, `all_but_last`. `first_n` and `last_n` need an `n`; `at_index` needs an `index`. The default is the first element of the outer list and all elements within it. ## See also - [Resolving conflicts](/merchandising/business-rules/conflicts/) — who wins when rules collide - [Triggers](/merchandising/business-rules/triggers/) — the condition syntax, and which requests fire - [Filters](/discovery/filters/) — the operator vocabulary and the missing-attribute rule