Project: Product Catalog (Attribute Pattern)

Introduction

E-commerce catalogs mix shared fields (name, SKU, price) with category-specific attributes (phone storage vs book ISBN). MongoDB’s flexible schema fits the Attribute pattern—one products collection with varying shape. This project models SKUs, queries with $elemMatch, and aggregates inventory.

Prerequisites

Project Goals

FeatureExample
Shared fieldsname, category, price, sku
Dynamic attributes{ key: "storage", value: "256GB" }
Variants arraycolor, size, stock per variant
Inventory statstotal stock by category

Document Shape

Phone:

Book:

Alternative: top-level dynamic keys with attributes.storage—pick one convention per team and document it.

Indexes

javascript
db.products.createIndex({ sku: 1 }, { unique: true })
db.products.createIndex({ category: 1, price: 1 })
db.products.createIndex({ "attributes.key": 1, "attributes.value": 1 })
db.products.createIndex({ "variants.sku": 1 })

Query: Find by Attribute

javascript
db.products.find({
  category: "electronics",
  attributes: {
    $elemMatch: { key: "storage", value: "256GB" }
  }
})

Find variant with low stock:

javascript
db.products.find({
  variants: {
    $elemMatch: { stock: { $lt: 10 } }
  }
})

Aggregation: Stock by Category

PyMongo: Update Variant Stock

python
from pymongo import ReturnDocument
 
def decrement_variant_stock(products, product_sku: str, variant_sku: str, qty: int = 1):
    return products.find_one_and_update(
        {"sku": product_sku, "variants.sku": variant_sku, "variants.stock": {"$gte": qty}},
        {"$inc": {"variants.$.stock": -qty}},
        return_document=ReturnDocument.AFTER,
    )

Positional $ updates the matched array element—atomic per document.

Warning

Multi-SKU orders spanning documents need transactions or idempotent order records—not blind multi-updates without checks.

Schema Validation (Optional)

Post-Project Checklist

  • Consistent attributes array shape
  • $elemMatch queries tested
  • Aggregation inventory report runs
  • Unique sku at product and variant level documented

FAQ

Attribute pattern vs many collections?

One collection simplifies cross-category search; separate collections per category help when schemas diverge completely.

Relational alternative?

Normalized products, attributes, variants tables in MySQL—more JOINs, stricter schema.

Search facets?

Atlas Search or text indexes on name; facet counts via $facetAdvanced Aggregation.

Price history?

Separate price_history collection with productId reference.

Cache hot SKUs?

Redis cache-aside on GET /products/{sku}.