Dev.to WebDev 🛠 Dev 👁 0 📖 2 min read

Smart Collections Without a Query Engine: Rules That Write Plain Membership Rows

Collections in most shops start as hand-picked lists. Someone adds products one by one, and the list goes stale as soon as stock changes or a new product arrives. Smart collections fix that with rules. The interesting pa

Collections in most shops start as hand-picked lists. Someone adds products one by one, and the list goes stale as soon as stock changes or a new product arrives. Smart collections fix that with rules. The interesting part is how we built them, because we deliberately did not build a query engine.

A rule is a small, closed set

A rule is a field, an operator, and a value. The fields are title (contains or equals), tag (is or is not), category (is), price (at least or at most), on sale (yes or no), in stock (yes or no), and new arrival (within 1 to 365 days). A collection matches all of its rules or any of them.

Validation rejects an unknown field, an operator the field does not support, a price that is not a number, a category that is not an id, and a new arrival window outside 1 to 365. Those checks run before anything is written, so a bad rule never becomes a collection that silently matches nothing.

Because the set is closed, every rule is evaluated in memory against a product's facts: title, tags, category, price, compare-at price, whether any variant is available, and creation time. We do not translate rules into SQL. The logic stays in one place and is easy to test.

Preview before you save

The editor calls a preview endpoint with the draft rules. It returns the count of matching products and a sample of up to twelve names, and it saves nothing. A store owner can check that a rule like tag is summer, price at most 50 matches what they expect before the collection goes live.

It writes the same rows as a manual collection

A refresh writes the same membership rows a manual collection uses. The admin, the storefront, and the counts already read those rows, so none of them needed to change. A refresh works out the wanted set, removes members that no longer match, and adds the new ones, newest first. Running it twice changes nothing, so it is safe to retry.

When it runs

A background job starts five minutes after the process boots, then refreshes every ten minutes. It takes a distributed lock first, so only one instance does the work in a multi-instance deployment. Saving rules also applies them straight away, and an admin can trigger a refresh by hand.

The ten minute delay is a real trade-off. A product that sells out can stay in a collection for up to ten minutes. We chose that over re-evaluating on every stock change, which would touch the whole catalogue constantly for every tenant.

Limits

Each refresh loads at most 20,000 active products per tenant, and the collection is computed only from those. The cap is a constant in the code, not a setting. A catalogue larger than that will not get a complete smart collection, so stores that big should stick to manual collections for now.

Rules cannot reference metafields, order history, or customer segments. Manual collections still cover anything the rules cannot express.

If you have built collection rules differently, I would like to compare approaches in the comments.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.