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

  1. Category page: term filter on category + status = active, sorted by name, paginated with limit/offset.
  2. Search box: query_bool_simple_query_string_field on name wrapped in query_bool_must, with the active-filter in query_bool_filter via query_and.
  3. Product page: fetch by sku with function_search_single_result; render variants from the embedded array, disabling out-of-stock sizes in the UI.
  4. Autocomplete: prefix/wildcard query on name behind a small API — the pattern in search optimization.

Stock Without Double-Selling

  1. Checkout decrements stock with mutations_increment_by_field (-quantity) inside function_update_document — mutation transactions process sequentially, so two checkouts can't overwrite each other.
  2. Guard the sale: only decrement when stock >= quantity; on the failure path, hold the order as backorder instead of rejecting the customer.
  3. Restock is the same increment with a positive value — one block, both directions.

Admin Side

  • Product management pages scoped *loggedin + catalog_manager custom 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.

Next Steps

Storage Examples The catalog and order models in full. Inventory Tracker Stock-focused variant of this example. Creating a REST API Expose the catalog to other systems.