Using Search Rules
Shoppers searching for drill should not always see the same ranking as every other query. Search rules let you boost or bury products, pin or hide specific products, rewrite a query, or add a filter when a shopper query — or a browse listing — matches a trigger you define.
Search rules live in rule groups. Each group is a named campaign container. You attach one or more groups to a search profile with the ordered rule_groups attribute. Until a group is attached, its rules have no effect on search.
Search rules are evaluated when a search runs, not when products are indexed. Creating or updating rules does not require a catalog reindex. Conditions and filters must still reference indexed fields — list indexed fields to see what is available, and enable custom attributes through indexable fields first if needed.
Always-On Boosts vs Query-Triggered Rules
Catalog Search supports two related merchandising tools on a search profile:
| Tool | When it applies | Configure with |
|---|---|---|
| Always-on boost / bury | Every search that uses the profile | Profile boosts |
| Search rules | Only when a rule trigger matches the shopper query or browse | Rule groups attached via profile rule_groups |
Use always-on boosts for storewide policies (for example, always promote in-stock featured products). Use search rules for query- or browse-specific campaigns (for example, boost Milwaukee when the shopper searches for drill).
When a search has both, rule boosts are resolved among themselves first, then combined with the profile boosts. Each product receives the single highest matching weight. See Profile boosts and rule boosts.
How Search Rules Work
- Create a rule group with a unique
slug. - Add rules to the group. Each rule has a trigger, one or more actions, an
execution_order, and an optionalstop_after_matchflag. - Attach the group UUID to a search profile's
rule_groupsarray. Earlier entries in the array run first across groups. - Search with that profile. Matching rules apply their actions; unmatched queries keep the profile's normal ranking.
Rules are evaluated in one profile-wide pipeline: profile rule_groups order, then each rule's execution_order inside that group (lower runs first). execution_order does not sort across groups.
A profile that lists the seasonal group before the brand group runs every seasonal rule before any brand rule:
| Group position | execution_order | When it runs |
|---|---|---|
| Seasonal (first) | 100 | 1st |
| Seasonal (first) | 200 | 2nd |
| Brand (second) | 50 | 3rd |
The brand rule stays third. Its lower execution_order applies only inside the brand group.
A non-empty rule_groups array turns on query-time rules for every search that uses that profile. Rule actions still apply only when a trigger matches. Set rule_groups to [] to return to profile-only behavior.
Triggers
Each rule has a trigger that decides when the rule matches.
match | When the rule runs | query |
|---|---|---|
exact | Normalized shopper query equals the trigger text | Required |
contains | Trigger text appears as a substring in the normalized query | Required |
browse | Non-autocomplete browse search (q=*), such as category or “view all” pages | Omit |
Query text is normalized before matching: trimmed, lowercased, and with whitespace collapsed.
| Trigger | Example | Matches | Does not match |
|---|---|---|---|
Exact drill | match: exact, query: drill | drill, Drill | cordless drill |
Contains drill | match: contains, query: drill | drill, cordless drill driver | hammer |
| Browse | match: browse | q=* browse listings | Typed search queries, autocomplete, empty or whitespace-only queries |
Actions
A rule must include at least one action.
Boost and bury
Boost and bury use the same condition syntax as profile boosts. Positive weights promote matching products. Negative weights bury them. A weight of 0 is neutral: the product ranks as if it matched no boost clause.
These two rules both match a shopper search for drill. The first moves featured products up by 100. The second moves clearance products down by -100.
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "boost-featured-drills",
"execution_order": 100,
"trigger": { "query": "drill", "match": "exact" },
"actions": {
"boosts": [
{ "condition": "extensions.products(Details).is_featured:=true", "weight": 100 }
]
}
}
}
}'
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "bury-clearance-drills",
"execution_order": 200,
"trigger": { "query": "drill", "match": "exact" },
"actions": {
"boosts": [
{ "condition": "extensions.products(Details).is_clearance:=true", "weight": -100 }
]
}
}
}
}'
Putting both weights in the boosts array of one rule produces the same checklist. The weights are sorted from the highest number to the lowest. Rule order and the order of objects in boosts do not set the check order. 100 is checked before -100:
| Check | From | Condition | Weight |
|---|---|---|---|
| 1 | boost-featured-drills | is_featured:=true | 100 |
| 2 | bury-clearance-drills | is_clearance:=true | -100 |
A shopper search for drill runs both rules:
curl "https://useast.api.elasticpath.com/v2/pcm/catalog/search?q=drill&search_profile=default" \
-H "Authorization: Bearer $ACCESS_TOKEN"
Each product starts at check 1 and stops at the first row it matches. That weight is the only one used.
| Product | Check 1, featured 100 | Check 2, clearance -100 | Weight used |
|---|---|---|---|
| Featured drill | Matches. Stop. | Not used | 100 |
| Featured clearance drill | Matches. Stop. | Not used | 100 |
| Clearance drill | No match | Matches. Stop. | -100 |
| Standard drill | No match | No match | none |
100 and -100 are never added. The featured clearance drill matches check 1, so the -100 from the clearance rule is skipped. When two rules use the same condition text, one weight is chosen before this checklist is built. See Same condition on more than one rule.
Same condition on more than one rule
When two matching rules use the same condition text, pipeline order picks one weight for that condition:
| Earlier rule | Later rule | Weight kept |
|---|---|---|
Positive or 0 | Positive or 0 | Earlier weight |
Boost or 0 | Bury (negative) | Later bury |
| Bury | More negative bury | More negative weight |
| Bury | Boost or 0 | Earlier bury |
An earlier rule that boosts BRAND-NAME:=Milwaukee by 100, and a later rule that buries that same condition by -100, leaves the condition at -100. Reverse those rules and the bury stays: a later boost on that condition does not restore the products.
Different conditions, including a zero-weight exception
Different condition text is all kept. Ordering by weight decides which clause a product hits first. When two clauses share a weight, the clause from the earlier rule is checked first.
Use 0 on a narrower, different condition to exempt products from a broader bury. 0 is ordered before every negative weight, so products that match the exception receive 0 and rank with products that matched no clause. The two clauses can sit on one rule, or on two matching rules. The conditions must be different text:
{
"boosts": [
{
"condition": "extensions.products(Details).brand:=Nike && extensions.products(Details).is_featured:=true",
"weight": 0
},
{
"condition": "extensions.products(Details).brand:=Nike",
"weight": -100
}
]
}
| Product | Brand | Featured | Clause it hits | Weight |
|---|---|---|---|---|
| Nike Air Max | Nike | yes | Featured Nike, checked first because 0 outranks -100 | 0 |
| Nike Clearance | Nike | no | Nike bury | -100 |
| Adidas Runner | Adidas | yes | No clause | none |
Nike Air Max stays with the ordinary results. Nike Clearance is buried. Adidas Runner is untouched.
The exception works because featured Nike products match a condition the other Nike products do not. Putting 0 on the same condition text, brand:=Nike, follows the table above instead:
Earlier weight on brand:=Nike | Later weight on brand:=Nike | Every Nike product |
|---|---|---|
0 | -100 | Buried at -100. The later bury replaces the 0. |
-100 | 0 | Buried at -100. The later 0 does not clear the bury. |
Profile boosts and rule boosts
Search rules are resolved with each other first. Always-on profile boosts are then combined with that result. For one condition, the higher weight is kept, whether it came from the profile or from a rule.
A storewide profile boosts Nike on every search, and a drill campaign buries Nike when the shopper searches for drill:
| Source | Condition | Weight |
|---|---|---|
Profile boosts | brand:=Nike | 100 |
| Matching search rule | brand:=Nike | -50 |
Both name brand:=Nike. 100 is higher than -50, so Nike products stay at 100 on that drill search. The rule bury does not demote them.
The same comparison runs the other way. A profile that buries out-of-season Nike, and a campaign rule that boosts Nike for this query:
| Source | Condition | Weight |
|---|---|---|
Profile boosts | brand:=Nike | -100 |
| Matching search rule | brand:=Nike | 50 |
50 is higher than -100, so Nike products receive 50 for this search. The profile bury does not apply to them on this query.
When more than one rule names that condition, those rules are reduced to one weight before the profile is considered. On brand:=Nike:
| Step | What is compared | Weight kept |
|---|---|---|
| Earlier rule | Boost 40 | 40 |
| Later rule | Bury -80 | -80. Among rules, the bury replaces the earlier boost. |
| Profile | Boost 100, compared with the merged rule weight -80 | 100. 100 is higher than -80. |
Nike products are boosted by the profile. The campaign bury never becomes the weight, because the profile clause is higher.
Different conditions from the profile and from rules are both kept. Each product still receives the highest clause it matches. A profile clause is_featured:=true at 100 and a rule clause is_clearance:=true at -100 produce:
| Product | Featured | Clearance | Weight |
|---|---|---|---|
| Featured clearance drill | yes | yes | 100 |
| Clearance drill | no | yes | -100 |
| Featured drill | yes | no | 100 |
Shopper-selected sort is the primary ordering. The boost step ranks products that sort leaves tied, and it occupies one sort field. The platform accepts at most three sort fields. When the shopper sort already uses three fields, the boost step is omitted and neither profile nor rule boosts change ranking. Test campaigns with the storefront sorts shoppers use. See How Results Are Ordered for how boosts combine with relevance and request sorts.
Pin and hide
Pins place products at one-based global result positions. Active filters still apply, so a pinned product that fails the current filters does not appear. A pin can include a product that does not match the query text when that product passes the filters.
When two matching rules pin different products to the same position, the earlier rule in the pipeline keeps that position. Within one rule, each product and each position can appear only once.
Hides remove products from results. Matching rules combine their hide lists. If the same product is both pinned and hidden, hide wins.
{
"pins": [
{ "product_id": "978dfdd0-7cbf-454d-90b4-3d61de60ca46", "position": 1 }
],
"hides": [
{ "product_id": "15b0b18e-48bb-47c8-96e8-3d5739e68dfd" }
]
}
Query rewrite
Replace the shopper query when the rule matches. Use this for common typos or alternate terms that should resolve to a canonical query.
The first rewrite in the pipeline is the replacement. That match ends the current pass, so later rules in the pass are not evaluated. Actions from rules that already matched in the pass still apply.
Matching then runs once more against the replacement. Rules that already ran are skipped. A replace_query on that second pass is ignored. stop_after_match ends only the pass it occurs in: a stop on the rewriting rule does not cancel the second pass, and a stop during the second pass skips later rules in that pass only.
A browse trigger cannot rewrite the query. The replacement must be non-empty text, and it cannot be *.
{
"replace_query": "drill"
}
Rule filter
Add an indexed-field filter only when the trigger matches. When several matching rules set filter_by, those expressions are ANDed together, then ANDed with profile filters and any shopper filters on the request.
{
"filter_by": "extensions.products(Details).is_in_stock:=true"
}
Ordering and stop_after_match
| Control | Scope | Behavior |
|---|---|---|
rule_groups array position | Across attached groups | Earlier UUID runs first |
execution_order | Within a group | Lower positive integer runs first; must be unique per group |
stop_after_match | Current matching pass | When true and the rule matches, later rules in that pass are skipped, including later groups |
stop_after_match applies that rule, keeps every earlier match in the pass, and skips the rest of the pass. A rule that does not match does not stop anything. After a query rewrite, the second pass still runs. See Query rewrite.
Use stop_after_match when a campaign must block broader rules that would otherwise also match.
Quick Reference
Rule groups and rules are managed through the following endpoints, relative to your API base URL (for example https://useast.api.elasticpath.com/v2):
| Operation | Method & Path |
|---|---|
| Create a rule group | POST /pcm/catalogs/search/rule-groups |
| List rule groups | GET /pcm/catalogs/search/rule-groups |
| Get a rule group | GET /pcm/catalogs/search/rule-groups/{rule_group_id} |
| Update a rule group | PUT /pcm/catalogs/search/rule-groups/{rule_group_id} |
| Delete a rule group | DELETE /pcm/catalogs/search/rule-groups/{rule_group_id} |
| Create a rule | POST /pcm/catalogs/search/rule-groups/{rule_group_id}/rules |
| List rules | GET /pcm/catalogs/search/rule-groups/{rule_group_id}/rules |
| Get a rule | GET /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id} |
| Update a rule | PUT /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id} |
| Delete a rule | DELETE /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id} |
| Move a rule | POST /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id}/move |
A rule group has these attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Unique identifier within the tenant. Pattern: ^[a-zA-Z0-9_.-]+$. |
description | string | No | Human-readable summary of the group's intent. |
A search rule has these attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
slug | string | No | Human-readable identifier within the group. |
description | string | No | Summary of what the rule does. |
execution_order | integer | Yes | Lower values run first within the group. Must be unique and at least 1. |
trigger | object | Yes | When the rule matches. See Triggers. |
actions | object | Yes | At least one of boosts, pins, hides, replace_query, or filter_by. |
stop_after_match | boolean | No | When true and this rule matches, skip later rules in the current pass, including later groups. Default false. |
JSON:API types are catalog_search_rule_group and catalog_search_rule.
Creating a Rule Group
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule_group",
"attributes": {
"slug": "seasonal-drill-campaign",
"description": "Merchandising rules for drill searches"
}
}
}'
Example response:
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "catalog_search_rule_group",
"attributes": {
"slug": "seasonal-drill-campaign",
"description": "Merchandising rules for drill searches"
},
"meta": {
"owner": "store",
"created_at": "2026-03-05T12:00:00Z",
"updated_at": "2026-03-05T12:00:00Z"
}
}
}
Creating Boost or Bury Rules
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "boost-featured-drills",
"execution_order": 100,
"trigger": { "query": "drill", "match": "exact" },
"actions": {
"boosts": [
{ "condition": "extensions.products(Details).is_featured:=true", "weight": 100 },
{ "condition": "extensions.products(Details).is_clearance:=true", "weight": -100 }
]
}
}
}
}'
Contains trigger example
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "boost-milwaukee-cordless",
"execution_order": 200,
"trigger": { "query": "cordless", "match": "contains" },
"actions": {
"boosts": [
{ "condition": "extensions.products(Details).BRAND-NAME:=Milwaukee", "weight": 50 }
]
}
}
}
}'
Pinning and Hiding Products
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "pin-and-hide-products",
"execution_order": 300,
"trigger": { "query": "cordless drill", "match": "contains" },
"actions": {
"pins": [
{ "product_id": "978dfdd0-7cbf-454d-90b4-3d61de60ca46", "position": 1 }
],
"hides": [
{ "product_id": "15b0b18e-48bb-47c8-96e8-3d5739e68dfd" }
]
}
}
}
}'
Browse Rules
A browse rule matches every non-autocomplete search with q=*: category pages, collection pages, and “view all” listings. Shopper filters on that listing still apply. Autocomplete does not evaluate browse rules. Exact and contains rules do not match q=*.
Profile field weights, synonyms, and typo tolerance rank typed search. A browse request has no query text for those settings to score. Listing order is the shopper’s sort, then boosts.
Profile boosts and filters run on every search that uses the profile, including keyword search. A browse rule keeps the same merchandising on listings. Attach one group to many profiles when those listings should share a policy and each profile should keep its own keyword tuning.
A browse rule matches every listing for the profiles that attach it. It does not target a single category. A boost or filter condition applies on each of those listings. On a shared condition, a higher profile boost still wins over a lower browse bury. Give the browse clause the higher weight, or leave that condition off the profile, when the listing policy must win.
| Goal | Browse rule | Why it stays off the profile |
|---|---|---|
| Rank listings on a profile that also serves search | Boost featured or high-margin products, bury clearance | Those clauses would also change keyword ranking |
| One listing policy on many profiles | Attach the same browse group to web, app, and wholesale profiles | Each profile keeps its own field weights and keyword rules. Edit the listing policy once |
| Full category grids, findable SKUs | filter_by in-stock | A profile filter would hide that SKU from search |
| Heroes and exclusions on grids | Pin the campaign product, hide parts and accessories | Those products stay searchable by name |
| A campaign on some profiles | Shared base browse group, plus a seasonal group listed first only on profiles in the campaign | Each profile chooses which groups to attach and in which order |
| Segment listings | Retail and wholesale profiles attach different browse groups | Keyword tuning stays on each profile. Only the listing policy diverges |
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "browse-featured-products",
"execution_order": 100,
"trigger": { "match": "browse" },
"actions": {
"filter_by": "extensions.products(Details).is_in_stock:=true",
"boosts": [
{ "condition": "extensions.products(Details).is_featured:=true", "weight": 100 }
]
}
}
}
}'
Query Rewrite Rules
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "rewrite-drill-typo",
"execution_order": 50,
"trigger": { "query": "dril", "match": "exact" },
"actions": { "replace_query": "drill" }
}
}
}'
Stopping Later Matching Rules
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"slug": "exclusive-sale-campaign",
"execution_order": 50,
"stop_after_match": true,
"trigger": { "query": "sale", "match": "exact" },
"actions": {
"boosts": [
{ "condition": "extensions.products(Details).is_sale:=true", "weight": 100 }
]
}
}
}
}'
Attaching Rule Groups to a Search Profile
A rule group has no effect until it is attached to a search profile:
curl -X PUT https://useast.api.elasticpath.com/v2/pcm/catalogs/search-profiles/{profile_id} \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_profile",
"attributes": {
"rule_groups": [
"550e8400-e29b-41d4-a716-446655440000",
"660e8400-e29b-41d4-a716-446655440000"
]
}
}
}'
Rules for attaching groups:
- A profile can reference multiple rule groups; array order sets cross-group precedence.
- A profile may only reference rule groups visible to its tenant (organization/store scope follows the same model as search profiles).
- Set
rule_groupsto[]to detach all groups and disable the query-time rules engine for that profile.
Updating and Moving Rules
Update a rule directly, including a sparse execution_order:
curl -X PUT https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules/8fccaa19-dba9-4621-8d11-31a222a68c7c \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"execution_order": 150,
"actions": {
"boosts": [
{ "condition": "extensions.products(Details).is_featured:=true", "weight": 150 }
]
}
}
}
}'
Move a rule relative to another rule in the same group:
curl -X POST https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules/8fccaa19-dba9-4621-8d11-31a222a68c7c/move \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"type": "catalog_search_rule",
"attributes": {
"placement": "before",
"target_rule_id": "a1000001-0001-4000-8000-000000000099"
}
}
}'
placement supports before, after, first, and last. Supply target_rule_id only for before and after.
The service preserves sparse execution_order values when it can (for example moving a rule into an unused integer gap). When no gap remains between neighboring rules, the group is renumbered so each rule still has a unique execution_order.
Matched Rules in Search Responses
When rules match a full search request, the response can include matched_rules with the rule id and parent rule_group_id. The payload lists identifiers only — not the full rule bodies — so storefronts and tooling can confirm which campaigns fired.
Autocomplete (search-as-you-type) does not evaluate search rules and does not return matched_rules, even when the selected search profile has rule groups attached.
Deleting Rules and Rule Groups
Delete a single rule:
curl -X DELETE https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000/rules/8fccaa19-dba9-4621-8d11-31a222a68c7c \
-H "Authorization: Bearer $ACCESS_TOKEN"
Deleting a rule is allowed even when its parent group is still attached to a profile.
Delete a rule group:
curl -X DELETE https://useast.api.elasticpath.com/v2/pcm/catalogs/search/rule-groups/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer $ACCESS_TOKEN"
A rule group cannot be deleted while any search profile still references it. Remove the group from every profile's rule_groups first, or the delete returns 409. Deleting a group cascade-deletes the rules inside it.
Organization and Store Scope
Rule groups follow the same organization and store tenancy model as search profiles:
- Organization-scoped groups can be attached to organization and store search profiles (subject to visibility rules for the tenant).
- Store-scoped groups can be attached only where that store's profiles allow.
Validation and Errors
| Status | When |
|---|---|
400 | Invalid slug, duplicate execution_order within a group, invalid trigger, empty actions, or invalid boost/filter syntax |
400 | Profile rule_groups contains an invalid UUID, a duplicate, or a group that does not exist / is out of scope |
403 | A store tries to update or delete an organization-scoped group or rule without permission |
404 | The rule group or rule does not exist |
409 | The rule group is still referenced by one or more search profiles |
Best Practices
- Attach groups intentionally. Creating rules alone does nothing until the group is on a profile's
rule_groupslist. - Choose the right trigger. Use
exactfor precise campaign queries,containsfor multi-word shopper phrasing, andbrowsefor category or all-products listings. - Plan group order and
execution_order. Pipeline order decides same-condition conflicts: an earlier boost or0beats a later boost or0, a later bury replaces an earlier boost, and an earlier bury stays in place. - Use
stop_after_matchsparingly. It skips later rules in the current pass. Rules that already matched still apply. It does not cancel the rematch that follows a query rewrite. - Merchandise only on indexed fields. Boost, bury, and rule filters cannot use sale windows or custom relationships until those fields are indexed.
- Test with real storefront sorts. Confirm campaigns under the same profile, filters, and shopper sort options production will use.
- Detach before deleting a group. Remove the group from every profile first to avoid a
409.