Example: E-Commerce Catalog
Products, variants, categories, stock, and storefront search — a complete catalog with the data model that makes it fast.
A catalog looks relational but shouldn't be modeled that way on RUAL: products carry everything their page shows. This example covers the model, the storefront, and stock handling that can't double-sell.
The Product Document
Product Document
{
"_meta": {},
"category": "apparel",
"image": "/static/products/tshirt-black.webp",
"name": "essential cotton t-shirt",
"price": 2490,
"sku": "TSHIRT-BLK-M",
"status": "active",
"variants": [
{
"size": "m",
"sku": "TSHIRT-BLK-M",
"stock": 14
},
{
"size": "l",
"sku": "TSHIRT-BLK-L",
"stock": 3
}
]
}- Variants live inside the product — a product page reads one document, never joins (see catalog model).
- Price in cents with
default_divide_by = 100— no float rounding bugs. - Everything stored lowercase so case-sensitive search just works.
The Storefront
- Category page: term filter on
category+status = active, sorted by name, paginated withlimit/offset. - Search box:
query_bool_simple_query_string_fieldonnamewrapped inquery_bool_must, with the active-filter inquery_bool_filterviaquery_and. - Product page: fetch by
skuwithfunction_search_single_result; render variants from the embedded array, disabling out-of-stock sizes in the UI. - Autocomplete: prefix/wildcard query on
namebehind a small API — the pattern in search optimization.
Stock Without Double-Selling
- Checkout decrements stock with
mutations_increment_by_field(-quantity) insidefunction_update_document— mutation transactions process sequentially, so two checkouts can't overwrite each other. - Guard the sale: only decrement when
stock >= quantity; on the failure path, hold the order asbackorderinstead of rejecting the customer. - Restock is the same increment with a positive value — one block, both directions.
Admin Side
- Product management pages scoped
*loggedin+catalog_managercustom scope — see User Authentication. - Price/name changes go through
function_update_document_mutations; images via the asset pipeline in File Upload & Processing. - Discontinuing is a status change, not a delete — old orders keep their embedded snapshots intact.
