
Merchandising Rules
- 61 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Control collection ordering with automated ranking rules, manual overrides, and performance-based sorting so the right products surface first.
About
Configures merchandising rules that control which products appear first in collections using automated ranking, manual pins, and performance-based sorting. A developer or merchandiser uses it to optimize product placement for conversion.
- Automated ranking rules with manual override/pinning
- Performance-based sorting algorithms
Merchandising Rules by the numbers
- 61 all-time installs (skills.sh)
- Ranked #3,152 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill merchandising-rulesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 61 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Control collection ordering with automated ranking rules, manual overrides, and performance-based sorting so the right products surface first.
Files
Merchandising Rules
Overview
Merchandising rules control which products appear first in your collections and search results. The goal is to surface products that are likely to convert — in-stock, popular, high-margin — while giving merchandisers manual control to pin hero products, hide out-of-stock items, and boost seasonal collections. Every major platform has some built-in sorting options; apps like Searchpie, Intelligems, or SearchPie add automated performance-based ranking.
When to Use This Skill
- When building product ranking logic for collection and category pages
- When creating automated (smart) collections based on product attributes, tags, or performance
- When implementing search result boosting and burying for merchandising control
- When adding pinning (manual placement) and slot-based merchandising to collection pages
- When measuring the revenue impact of different product ranking strategies
Core Instructions
Step 1: Determine your platform and choose the right merchandising tool
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Shopify's built-in collection sorting + Kimonix or SearchPie | Shopify has built-in sort options; Kimonix and SearchPie add performance-based automated sorting with manual override capability |
| WooCommerce | WooCommerce's default sort + YITH WooCommerce Catalog Mode or WooCommerce Product Table | WooCommerce supports basic sorting; YITH and similar plugins add advanced catalog control |
| BigCommerce | Built-in Collection Sorting + SearchPie or Boost Commerce | BigCommerce has strong built-in category sorting; Boost Commerce adds advanced search merchandising |
| Custom / Headless | Algolia or Elasticsearch with a merchandising rules layer | Algolia has a built-in "Rules" and "Pinning" feature in its dashboard; Elasticsearch needs custom scoring rules |
Step 2: Configure basic collection sorting
Shopify
Built-in sorting options (no app needed): 1. Go to Products → Collections → [Collection] → Products 2. From the "Sort" dropdown, choose:
- Best Selling — sorts by total units sold historically (most popular first)
- Newest — most recently added products first
- Price (Low to High / High to Low) — price-based sorting
- Manually — lets you drag products to specific positions
3. The "Manual" sort lets you pin specific products by dragging them to the top — useful for hero products and new launches
Smart (automated) collections: 1. Go to Products → Collections → Create collection 2. Set type to Automated 3. Add conditions: e.g., "Tag is equal to 'summer'" or "Product price is greater than $50" 4. Shopify automatically adds/removes products that meet the conditions 5. Set the sort order for the smart collection (Best Selling, Newest, etc.)
Shopify Search & Discovery app (free): 1. Install the Shopify Search & Discovery app from the App Store (free, by Shopify) 2. This app lets you boost specific products in search results and collection pages 3. Go to the app → Collections → [Collection] → Boost products to pin products to the top 4. Go to Bury products to push low-priority items (clearance, out-of-season) to the bottom
WooCommerce
Built-in sorting: 1. Go to WooCommerce → Settings → Products → Default product sorting 2. Options: Default (custom ordering), Popularity, Average Rating, Latest, Price (Low to High) 3. "Custom ordering" lets you drag products to specific positions in WooCommerce → Products 4. Enable multiple sort options for customers in WooCommerce → Settings → Products → Enable sorting
Category product display (manual control): 1. Go to Products → Categories → [Category] → Products tab 2. Drag products to reorder them within the category page 3. For programmatic control: install WooCommerce Custom Order / Alphabetical Order plugin
BigCommerce
1. Go to Products → Product Categories → [Category] 2. Click the Sort tab to set the default sort order for the category 3. Options: Name, Price, Date, Sales (best selling), Featured, Manual 4. "Manual" sort allows dragging products into specific positions 5. "Featured" sort shows products marked as "Featured" first, then falls back to the default
BigCommerce Search:
- BigCommerce's search returns results based on relevance by default
- To boost specific products in search: mark them as "Featured" or add keywords in the product SEO fields
- For advanced search merchandising: install Boost Commerce from the BigCommerce App Marketplace
Step 3: Set up performance-based automated ranking
Performance-based ranking automatically promotes products that are selling well and pushes down slow-movers. This typically requires an app.
Shopify — Kimonix
1. Install Kimonix from the Shopify App Store (paid, starts ~$30/month) 2. Kimonix connects to your Shopify analytics and builds a score for each product based on:
- Sales velocity (recent units sold)
- Conversion rate (add-to-cart rate from collection)
- Inventory level (deprioritize products about to go out of stock)
- Margin (optional, if you've entered cost data)
3. Configure the weighting in Kimonix → Strategies: e.g., 50% sales velocity, 30% conversion rate, 20% inventory 4. Kimonix re-sorts the collection automatically on a schedule (hourly, daily, or triggered by events) 5. Override: manually pin products to specific positions — Kimonix respects manual pins and sorts the rest by algorithm
Using Shopify Search & Discovery (free) for simpler boosts: 1. Open the Search & Discovery app 2. Under Collections, select a collection and click "Add boost" 3. You can boost by product tag (e.g., boost all products tagged "new-arrival"), specific products, or product type 4. "Bury" works the same way for clearance items or out-of-season products
WooCommerce
Using YITH WooCommerce Ajax Product Filter + WooCommerce Visual Products Configurator:
- For basic automated sorting based on sales: WooCommerce's built-in "Sort by Popularity" uses a
total_salesmeta field that increments with each sale - For advanced scoring: install Booster for WooCommerce which adds weighted product sorting based on custom criteria
BigCommerce — Boost Commerce
1. Install Boost Commerce from the BigCommerce App Marketplace 2. Boost Commerce adds smart sorting (by revenue, conversion rate, or custom score) to your category pages 3. Create "Merchandising Rules" in Boost Commerce to pin, boost, or bury specific products 4. Set up time-limited rules for seasonal campaigns (e.g., boost "winter" tagged products Dec–Feb)
Step 4: Set up search result merchandising
Shopify
Shopify Search & Discovery — search boosts: 1. Open the Search & Discovery app → Search 2. Under "Boosts", click "Add boost" for specific search queries 3. Example: for the query "jacket", boost products tagged "featured-jacket" to the top 4. Under "Synonyms": add common spelling variations (e.g., "t-shirt" = "tshirt" = "tee") 5. Under "Product filters": configure which filters appear on search results (price, color, size, etc.)
WooCommerce
- WooCommerce Product Search plugin (WooCommerce.com) adds relevance-based search
- Configure search weights: title weight = 5, tag weight = 3, category weight = 2, description weight = 1
- For advanced merchandising: use SearchWP with its WooCommerce integration to create custom search rules
Custom / Headless — Algolia
1. Sign up at algolia.com and install the Algolia client in your store 2. Push your product catalog to Algolia with a background job 3. In Algolia's dashboard → Rules, create merchandising rules:
- Pin a product to position 1 for a specific query
- Boost products with a specific attribute (e.g.,
in_stock: true) - Bury products with
clearance: true
4. Custom ranking: in Algolia → Indices → Ranking → Custom Ranking, add:
desc(sales_30d)— sort by recent sales descendingdesc(conversion_rate)— secondary sort by conversion rate
5. Algolia updates rankings in real time as you push new product metrics to the index
Step 5: Custom / Headless — scoring engine
// Compute a merchandising score for a set of products
interface ProductMetrics {
productId: string;
unitsSold30d: number;
revenue30d: number;
conversionRatePct: number; // (add-to-carts / page views) × 100
grossMarginPct: number;
daysInStock: number; // 0 = out of stock
daysSinceAdded: number;
}
interface ScoreWeights {
salesVelocity: number; // e.g., 0.35
revenue: number; // e.g., 0.20
conversion: number; // e.g., 0.20
margin: number; // e.g., 0.15
recency: number; // e.g., 0.10 (newer products get a boost)
}
function scoreProducts(metrics: ProductMetrics[], weights: ScoreWeights): { productId: string; score: number }[] {
// Normalize each metric to 0–1 range across the set
const normalize = (values: number[]) => {
const min = Math.min(...values);
const max = Math.max(...values);
return values.map(v => max === min ? 0.5 : (v - min) / (max - min));
};
const salesScores = normalize(metrics.map(m => m.unitsSold30d));
const revenueScores = normalize(metrics.map(m => m.revenue30d));
const conversionScores = normalize(metrics.map(m => m.conversionRatePct));
const marginScores = normalize(metrics.map(m => m.grossMarginPct));
// Recency: newer = higher score (invert daysAdded)
const recencyScores = normalize(metrics.map(m => -m.daysSinceAdded));
return metrics.map((m, i) => {
const score =
salesScores[i] * weights.salesVelocity +
revenueScores[i] * weights.revenue +
conversionScores[i] * weights.conversion +
marginScores[i] * weights.margin +
recencyScores[i] * weights.recency;
// Out-of-stock products get pushed to the bottom
const adjustedScore = m.daysInStock === 0 ? score - 2 : score;
return { productId: m.productId, score: Math.round(adjustedScore * 1000) / 1000 };
}).sort((a, b) => b.score - a.score);
}Best Practices
- Bury out-of-stock products, don't exclude them — out-of-stock products should still appear in search (for SEO and wishlists) but rank lower; only completely remove discontinued products
- Refresh scores daily, not on every page load — pre-compute and store product scores; computing scores in real time on every collection request is too slow
- Pin hero products and new arrivals manually — automated scoring is good for the long tail; manually pin the 3–5 key products you're actively promoting each season
- Provide a preview mode before applying new rules — the Shopify Search & Discovery app and Kimonix both offer preview functionality; use it to verify the collection order before pushing live
- Cap the number of pinned products — if you pin too many products, the algorithm never gets to show other products; keep manual pins to ≤ 5 per collection
Common Pitfalls
| Problem | Solution |
|---|---|
| Best-selling products dominate every collection indefinitely | Add a recency weight and a "new product boost" for items added in the last 14 days; this gives new products a chance to get exposure |
| Merchandising rule conflicts with another rule | In Shopify Search & Discovery and Kimonix, rules have priority order — define which rules override others and document the priority scheme |
| Smart collection adds products that shouldn't be there | Review your collection conditions carefully; using "any condition" instead of "all conditions" is the most common cause of unintended inclusions |
| Performance scores skewed by a single viral day | Use a rolling 30-day window for sales metrics, not all-time totals; cap extreme outliers at the 95th percentile |
Related Skills
- @multi-channel-selling
- @demand-forecasting
{
"context": "Tests whether the agent correctly implements the collection ranking pipeline: boost/bury score formulas, exclusion via map deletion, manual pinning with score 999 and 1-based positions, and the correct ordering of operations.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Boost formula",
"max_score": 10,
"description": "Boost increases a product's score by adding (boostValue / 100) to the current score (e.g. score + 40/100 for a +40 boost), using 50 as default if no value specified"
},
{
"name": "Bury formula",
"max_score": 10,
"description": "Bury decreases a product's score by subtracting (buryValue / 100) from the current score (e.g. score - 60/100 for a -60 bury), using 50 as default if no value specified"
},
{
"name": "Exclusion removes from scores",
"max_score": 10,
"description": "Excluded products are removed from the scores map/structure entirely (not set to 0 or -1), so they do not appear in output"
},
{
"name": "Pinned score 999",
"max_score": 8,
"description": "Pinned products are assigned a score of 999 (not just placed at their position)"
},
{
"name": "isPinned flag",
"max_score": 8,
"description": "Pinned products have an isPinned: true flag (or equivalent boolean field) in the output"
},
{
"name": "1-based position insert",
"max_score": 10,
"description": "A pin to position N inserts the product at array index N-1 (1-based), and the insert index is clamped to the array length if N exceeds the total product count"
},
{
"name": "Score-descending sort",
"max_score": 8,
"description": "Non-pinned products are sorted in descending score order before pins are applied"
},
{
"name": "Pins processed in position order",
"max_score": 8,
"description": "When multiple pins exist, they are inserted in ascending position order (pin to slot 1 before slot 3) to avoid index shifting conflicts"
},
{
"name": "Scoring before overrides",
"max_score": 10,
"description": "Boost/bury rules are applied to scores before manual pin/exclude overrides are processed (correct pipeline order)"
},
{
"name": "Priority order for rules",
"max_score": 8,
"description": "Boost/bury rules are applied in priority order (higher priority number = applied first) or the implementation otherwise defines explicit rule precedence"
},
{
"name": "Duplicate pin removal",
"max_score": 10,
"description": "If a pinned product already appears in the sorted list, it is removed from its current position before being inserted at the pin position"
}
]
}
Implement Collection Product Ranking with Merchandising Overrides
Problem/Feature Description
A home goods retailer runs a flagship "Living Room" collection page that drives 30% of their revenue. Their merchandising manager needs fine-grained control over how products appear: certain high-margin items should be promoted to the front, clearance items should be pushed to the back, and a few key hero products must always appear at specific positions (e.g., their new sofa launch always at slot 1 for the next two weeks).
The engineering team needs to implement the collection ranking module that takes a pre-scored list of products, applies configurable boost/bury rules, and then applies manual placement overrides. The system should correctly handle products being excluded (e.g., discontinued items), products being pinned to specific positions, and the ordering of all remaining products by score after overrides are applied.
Output Specification
Implement the collection ranking pipeline in collection_ranker.ts (or equivalent language). The implementation should:
1. Accept a list of products with scores, a list of boost/bury rules (each with conditions and a value), and a list of manual overrides (pin/bury/exclude) 2. Apply the boost/bury rules to adjust scores 3. Apply manual overrides (exclusions and pins) 4. Return products in final ranked order with each product's final score and a flag indicating whether it is pinned
Save a demonstration script to ranking_demo.ts (or equivalent) that:
- Creates a sample set of 8 products with scores and varied tags/attributes
- Defines at least 2 boost/bury rules (e.g., boost "new-arrival" tag, bury "clearance" tag)
- Defines at least 2 manual overrides (pin one product to position 1, exclude one discontinued product)
- Runs the full pipeline and prints the final ordered list with scores and pin status
Run the demo and save the output to ranking_output.txt.
{
"context": "Tests whether the agent implements a product scoring engine following the prescribed default weights, normalization approach, score rounding, recency inversion, and inventory capping logic.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Default salesVelocity weight",
"max_score": 8,
"description": "Default weight for salesVelocity (or equivalent recent-sales metric) is 0.30"
},
{
"name": "Default revenue weight",
"max_score": 8,
"description": "Default weight for revenue is 0.20"
},
{
"name": "Default conversionRate weight",
"max_score": 8,
"description": "Default weight for conversion rate is 0.20"
},
{
"name": "Default margin weight",
"max_score": 8,
"description": "Default weight for margin (profit) is 0.15"
},
{
"name": "Default recency weight",
"max_score": 6,
"description": "Default weight for recency (newness) is 0.10"
},
{
"name": "Default inventory weight",
"max_score": 6,
"description": "Default weight for inventory level is 0.05"
},
{
"name": "Min-max normalization",
"max_score": 10,
"description": "Each metric is normalized to 0-1 range using min-max normalization across the product set before computing scores"
},
{
"name": "Equal-range handling",
"max_score": 8,
"description": "When all products have the same value for a metric (range min == max), the normalized value is 0.5 rather than causing a divide-by-zero error"
},
{
"name": "Recency inversion",
"max_score": 12,
"description": "Recency is inverted so newer products (fewer days since creation) receive a higher score component (e.g. 1 - normalized_days_since_created)"
},
{
"name": "Inventory cap at 0.8",
"max_score": 10,
"description": "Inventory score is capped at a maximum of 0.8 (not rewarding overstock)"
},
{
"name": "Zero inventory yields 0",
"max_score": 8,
"description": "Products with zero inventory quantity receive an inventory score of 0 (not just a low score)"
},
{
"name": "Score rounding",
"max_score": 8,
"description": "Final scores are rounded to 3 decimal places (e.g. using Math.round(score * 1000) / 1000 or equivalent)"
}
]
}
Build a Product Scoring Engine for an E-commerce Platform
Problem/Feature Description
Your team is building the core ranking infrastructure for a mid-sized fashion e-commerce platform that sells across 15 product categories. The merchandising team currently ranks products manually, which is time-consuming and doesn't scale. They want an automated system that scores each product based on its business performance, so the most valuable products naturally surface to the top of collection pages.
The scoring system needs to combine several product performance signals: how fast a product has been selling recently, how much revenue it generates, its conversion rate, its profit margin, how new it is, and how much stock is available. The merchandising lead has emphasized that the system should handle edge cases gracefully, particularly around out-of-stock products and brand-new products that haven't built up a sales history yet.
Output Specification
Implement a ProductScoringEngine class in TypeScript (or equivalent in another language) that: 1. Accepts a list of product metrics and optional weight configuration 2. Returns a map of product ID to computed score
Save your implementation to scoring_engine.ts (or .py, .js etc. if you prefer a different language).
Also create a scoring_demo.ts (or equivalent) that:
- Creates sample data for at least 5 products with varied metrics (including at least one out-of-stock product and one brand-new product with no sales)
- Runs the scoring engine on the sample data
- Prints each product's final score to stdout
Run the demo and save the output to scoring_output.txt.
Input Files
No external files are required — implement the engine from scratch using the product metrics described above.
{
"context": "Tests whether the agent builds an Elasticsearch function_score query following the prescribed structure, score modes, default boosts, field weights, and fuzziness, plus whether audit logging captures the required fields.",
"type": "weighted_checklist",
"checklist": [
{
"name": "function_score structure",
"max_score": 8,
"description": "The query uses an Elasticsearch function_score query structure (top-level key is 'function_score')"
},
{
"name": "score_mode multiply",
"max_score": 8,
"description": "The function_score query sets score_mode to 'multiply'"
},
{
"name": "boost_mode multiply",
"max_score": 8,
"description": "The function_score query sets boost_mode to 'multiply'"
},
{
"name": "Weight multiplier formula",
"max_score": 12,
"description": "Merchandising boost percentages are converted to weight multipliers using 1 + (value / 100) — e.g. a +60 boost becomes weight 1.6"
},
{
"name": "multi_match base query",
"max_score": 6,
"description": "The base query inside function_score is a multi_match query"
},
{
"name": "Field boosts title^3 tags^2",
"max_score": 8,
"description": "multi_match includes title with boost ^3 and tags with boost ^2 (i.e. 'title^3' and 'tags^2' in the fields array)"
},
{
"name": "fuzziness AUTO",
"max_score": 6,
"description": "multi_match uses fuzziness: 'AUTO'"
},
{
"name": "Default in-stock boost",
"max_score": 10,
"description": "A default function is always added that boosts in-stock products (inventory_quantity > 0) with weight 1.5"
},
{
"name": "Default image boost",
"max_score": 8,
"description": "A default function is always added that boosts products with a featured_image field with weight 1.1"
},
{
"name": "Audit userId",
"max_score": 6,
"description": "Audit log entry includes the admin user ID (userId or equivalent field)"
},
{
"name": "Audit action and resource",
"max_score": 10,
"description": "Audit log entry includes both an action name (e.g. 'merchandising_rule_created') and a resource identifier referencing the rule (e.g. 'rule:<id>')"
},
{
"name": "Audit rule details",
"max_score": 10,
"description": "Audit log entry includes rule details such as rule name and rule type"
}
]
}
Build Elasticsearch Search Merchandising Query Builder
Problem/Feature Description
A sports apparel company uses Elasticsearch to power their storefront search. Their merchandising team wants to apply promotional boosts during a summer campaign: products tagged as "summer" or "outdoor" should rank higher in search results, while still using Elasticsearch's relevance scoring as the foundation. They also need to ensure that in-stock products and products with images always get a baseline boost over those that don't.
You need to build the function that constructs the Elasticsearch query given a search term and a list of active merchandising boost rules. The resulting query object should be ready to send to Elasticsearch's _search endpoint.
Additionally, the head of merchandising wants a simple admin logging mechanism: every time a new merchandising rule is created through the API, the action should be recorded with enough context to later audit who changed what and when.
Output Specification
Create search_query_builder.ts (or equivalent language) containing:
1. A function buildSearchQuery(searchTerm, boostRules) that produces an Elasticsearch query object 2. A function logRuleCreation(adminUserId, rule) (or equivalent) that records a structured audit log entry so that changes can later be reviewed by the compliance team
Save a demonstration script to search_demo.ts (or equivalent) that:
- Builds a sample query for the search term "running shoes" with two boost rules (e.g., boost products tagged "summer" by +60, boost vendor "Nike" by +30)
- Calls the audit log function to record a rule creation event
- Prints the generated Elasticsearch query as formatted JSON to stdout
- Writes the audit log entries to
audit_log.json
Run the demo and save the query output to search_output.json.
{
"name": "finsi/merchandising-rules",
"version": "0.1.0",
"summary": "Visual merchandising, product ranking rules, and automated collection curation",
"skills": {
"merchandising-rules": {
"path": "SKILL.md"
}
}
}