B2B catalogs for industrial materials look boring until you try to make them queryable. A "steel strip" isn't one product — it's a point in a multi-dimensional space of grade, thickness, width, temper and finish. Here's how to model that without drowning in a combinatorial explosion of SKUs.
Don't enumerate variants — describe axes
The naive approach creates one row per physical variant, and a mid-size strip supplier ends up with tens of thousands of near-duplicate SKUs. Instead, separate the product family from its dimensional axes:
from dataclasses import dataclass
@dataclass(frozen=True)
class StripSpec:
grade: str # e.g. "08X18H10", "12X18H10T", "cold-rolled-08kp"
thickness_mm: float
width_mm: float
temper: str # annealed / hard / half-hard
finish: str # 2B, BA, matte
A concrete offering is a StripSpec plus stock and price; the family ("cold-rolled strip", "stainless strip") groups them for browsing.
Grades are an enum with metadata, not free text
Stainless grades like 12X18H10T and 08X18H10 map to standardized compositions. Store them as a lookup keyed by canonical grade code, with cross-references (GOST / AISI equivalents) as data:
GRADES = {
"12X18H10T": {"aisi": "321", "family": "austenitic", "c_max": 0.12},
"08X18H10": {"aisi": "304", "family": "austenitic", "c_max": 0.08},
}
Now "show me austenitic strips under 1mm" is a filter, not a full-text search.
Range queries need half-open intervals
Thickness and width are continuous, so users query ranges, not equality. Index them as numeric columns and always use half-open intervals [lo, hi) to avoid the double-counting bug at boundaries when a strip sits exactly on a grid line.
Real-world reference
Looking at how an actual supplier organizes its range is a useful sanity check before you fix your schema. A catalog such as здесь lays out cold-rolled and stainless strip by grade and dimension, which is a good reference for the axes worth exposing as filters versus the ones better left as free attributes.
Takeaway
Model the axes, not the variants; make grades a metadata-carrying enum with standard cross-references; and treat dimensions as indexed numeric ranges. The catalog stays small, and every "do you have X in Y?" question becomes a query instead of a scan.
Top comments (0)