Skip to main content

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.

No reindexing required

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:

ToolWhen it appliesConfigure with
Always-on boost / buryEvery search that uses the profileProfile boosts
Search rulesOnly when a rule trigger matches the shopper query or browseRule 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).

Prefer one clear strategy for a given outcome. When both apply, verify ranking with the same profile, query, filters, and shopper sort your storefront will use.

How Search Rules Work

  1. Create a rule group with a unique slug.
  2. Add rules to the group. Each rule has a trigger, one or more actions, an execution_order, and an optional stop_after_match flag.
  3. Attach the group UUID to a search profile's rule_groups array. Earlier entries in the array run first across groups.
  4. 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 (lower runs first).

Attaching rule groups activates the rules engine

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.

matchWhen the rule runsquery
exactNormalized shopper query equals the trigger textRequired
containsTrigger text appears as a substring in the normalized queryRequired
browseNon-autocomplete browse search (q=*), such as category or “view all” pagesOmit

Query text is normalized before matching: trimmed, lowercased, and with whitespace collapsed.

TriggerExampleMatchesDoes not match
Exact drillmatch: exact, query: drilldrill, Drillcordless drill
Contains drillmatch: contains, query: drilldrill, cordless drill driverhammer
Browsematch: browseq=* browse listingsTyped search queries, autocomplete

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; 0 is a neutral exception (useful to exempt a narrower set from a broader bury).

{
"boosts": [
{ "condition": "extensions.products(Details).is_featured:=true", "weight": 100 },
{ "condition": "extensions.products(Details).is_clearance:=true", "weight": -100 }
]
}

When several matching rules set overlapping boost/bury conditions:

  • Distinct conditions are merged.
  • For the same condition, the earlier rule wins.
  • Bury overrides boost when they conflict.
  • Stronger bury wins over weaker bury.
  • A narrower weight of 0 can exempt products from a broader bury.

Shopper-selected sort remains the primary ordering. Rule boost/bury ranks otherwise comparable results. If a shopper sort already uses the maximum number of sort fields supported by the platform, rule boost/bury may not affect ranking — test important campaigns with the storefront sorts shoppers actually use.

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. Hides remove products from results. 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. The first rewrite wins — if several matching rules set replace_query, only the earliest rewrite in the profile-wide pipeline is applied. Rule matching then runs once more against that replacement text. Use this for common typos or alternate terms that should resolve to a canonical query.

{
"replace_query": "drill"
}

Rule filter

Add an indexed-field filter only when the trigger matches. The expression is 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

ControlScopeBehavior
rule_groups array positionAcross attached groupsEarlier UUID runs first
execution_orderWithin a groupLower positive integer runs first; must be unique per group
stop_after_matchProfile-wide pipelineWhen true and the rule matches, later rules (including later groups) are skipped

Use stop_after_match when a specific campaign must take exclusive precedence over 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):

OperationMethod & Path
Create a rule groupPOST /pcm/catalogs/search/rule-groups
List rule groupsGET /pcm/catalogs/search/rule-groups
Get a rule groupGET /pcm/catalogs/search/rule-groups/{rule_group_id}
Update a rule groupPUT /pcm/catalogs/search/rule-groups/{rule_group_id}
Delete a rule groupDELETE /pcm/catalogs/search/rule-groups/{rule_group_id}
Create a rulePOST /pcm/catalogs/search/rule-groups/{rule_group_id}/rules
List rulesGET /pcm/catalogs/search/rule-groups/{rule_group_id}/rules
Get a ruleGET /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id}
Update a rulePUT /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id}
Delete a ruleDELETE /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id}
Move a rulePOST /pcm/catalogs/search/rule-groups/{rule_group_id}/rules/{rule_id}/move

A rule group has these attributes:

AttributeTypeRequiredDescription
slugstringYesUnique identifier within the tenant. Pattern: ^[a-zA-Z0-9_.-]+$.
descriptionstringNoHuman-readable summary of the group's intent.

A search rule has these attributes:

AttributeTypeRequiredDescription
slugstringNoHuman-readable identifier within the group.
descriptionstringNoSummary of what the rule does.
execution_orderintegerYesLower values run first within the group. Must be unique and at least 1.
triggerobjectYesWhen the rule matches. See Triggers.
actionsobjectYesAt least one of boosts, pins, hides, replace_query, or filter_by.
stop_after_matchbooleanNoWhen true, skip later matching rules after this rule matches. 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

Browse rules apply to category pages, collection pages, and “view all products” experiences where the storefront sends q=*. They can still combine with shopper filters on that listing. They do not apply to autocomplete.

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_groups to [] 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.

Search rules are not applied to autocomplete

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

StatusWhen
400Invalid slug, duplicate execution_order within a group, invalid trigger, empty actions, or invalid boost/filter syntax
400Profile rule_groups contains an invalid UUID, a duplicate, or a group that does not exist / is out of scope
403A store tries to update or delete an organization-scoped group or rule without permission
404The rule group or rule does not exist
409The rule group is still referenced by one or more search profiles

Best Practices

  1. Attach groups intentionally. Creating rules alone does nothing until the group is on a profile's rule_groups list.
  2. Choose the right trigger. Use exact for precise campaign queries, contains for multi-word shopper phrasing, and browse for category or all-products listings.
  3. Plan execution_order. Give overlapping rules intentional order so the earlier rule wins conflicts.
  4. Use stop_after_match sparingly. Reserve it for exclusive campaigns that must block later matching rules.
  5. Merchandise only on indexed fields. Boost, bury, and rule filters cannot use sale windows or custom relationships until those fields are indexed.
  6. Test with real storefront sorts. Confirm campaigns under the same profile, filters, and shopper sort options production will use.
  7. Detach before deleting a group. Remove the group from every profile first to avoid a 409.
Ask External AI