TechnicalJanuary 2025 · 9 min read

Flattening Akeneo Product Models for Database Export: The Complete Guide

If you've ever tried to export Akeneo products to a database and ended up with half-empty records missing parent attributes, this guide explains why — and how to fix it.

Understanding Akeneo's 3-level product hierarchy

Akeneo uses a hierarchical model for configurable products (products with variants like size or color). The hierarchy has three levels:

Level 1 — Root Product Model
  identifier: "TSHIRT"
  family_variant: "clothing_by_color_size"
  attributes at this level:
    - description (same for all variants)
    - care_instructions
    - brand
  ↓

Level 2 — Sub Product Model (varies by color)
  identifier: "TSHIRT-BLUE"
  attributes at this level:
    - color = "blue"
    - images (color-specific photos)
  ↓

Level 3 — Simple Product (variant, has SKU + stock)
  identifier: "TSHIRT-BLUE-M"
  attributes at this level:
    - size = "M"
    - ean = "1234567890123"
    - price = [{ amount: "29.99", currency: "EUR" }]
The problem: Akeneo's /products API endpoint returns only Level 3 products. When you fetch TSHIRT-BLUE-M, you get size = "M" and color = "blue" — but NOT description, care_instructions, or brand from Level 1. Those live on the Product Model.

Why flattening is necessary for MongoDB and SQL databases

Most database use cases need each product record to be self-contained — a single document or row with all attributes resolved. Your e-commerce frontend shouldn't have to join across three levels at query time.

Without flattening

TSHIRT-BLUE-M:

size: "M"

color: "blue"

description: undefined ❌

brand: undefined ❌

After flattening

TSHIRT-BLUE-M:

size: "M"

color: "blue"

description: "Classic..." ✓

brand: "BrandName" ✓

How SyncPIM's intelligent variant flattening works

SyncPIM resolves the full hierarchy in three steps:

  1. 1

    Fetch product models separately

    SyncPIM calls /product-models in addition to /products, building an in-memory map of all parent models keyed by identifier.

  2. 2

    Build the parent chain for each variant

    For each Simple Product, SyncPIM looks up its parent (the Sub Product Model), then the grandparent (the Root Product Model), building a chain: [root → sub → variant].

  3. 3

    Merge attributes bottom-up

    Starting from the root, SyncPIM merges attributes down the chain. Child attributes override parent attributes when the same attribute exists at multiple levels. The variant's own attributes always win.

Attribute inheritance: what gets merged vs overridden

The merge follows a simple rule: child attributes override parent attributes when both define the same attribute code. If the child doesn't define an attribute, the parent's value is inherited.

// Root Product Model: TSHIRT
{
  description: "Classic cotton t-shirt",  // ← inherited by all variants
  brand: "Basics Co.",                    // ← inherited
  care_instructions: "Machine wash cold", // ← inherited
}

// Sub Product Model: TSHIRT-BLUE
{
  color: "blue",                          // ← overrides nothing (new attr)
  images: ["blue-front.jpg", "blue-back.jpg"], // ← new attr
}

// Variant: TSHIRT-BLUE-M (after flattening)
{
  description: "Classic cotton t-shirt",  // from Root
  brand: "Basics Co.",                    // from Root
  care_instructions: "Machine wash cold", // from Root
  color: "blue",                          // from Sub
  images: ["blue-front.jpg", ...],        // from Sub
  size: "M",                              // own attr
  ean: "1234567890123",                   // own attr
  price: [{ amount: "29.99", ... }],      // own attr
}

Example output: a flattened clothing product with all variants

A single t-shirt root model with 2 colors × 3 sizes produces 6 separate documents in MongoDB (or 6 rows in PostgreSQL), each fully self-contained:

// 6 documents/rows — one per buyable variant:
TSHIRT-BLUE-S  → { color: blue, size: S, price: 29.99, description: "...", ... }
TSHIRT-BLUE-M  → { color: blue, size: M, price: 29.99, description: "...", ... }
TSHIRT-BLUE-L  → { color: blue, size: L, price: 29.99, description: "...", ... }
TSHIRT-RED-S   → { color: red,  size: S, price: 27.99, description: "...", ... }
TSHIRT-RED-M   → { color: red,  size: M, price: 27.99, description: "...", ... }
TSHIRT-RED-L   → { color: red,  size: L, price: 27.99, description: "...", ... }

Note: red variants have a different price (€27.99) defined at the Sub Product Model level — SyncPIM correctly resolves this per-color price override.

See flattening in action with your catalog

10 free exports. SyncPIM automatically flattens all product models — no configuration needed.

Related