dataLayer a recursive merge

dataLayer je jednoduché javascriptové pole. Pushnu objekt, přečtu si ho v proměnné v Google Tag Manageru (GTM), hotovo. Na tom přece nemůže být nic složitého.

Jasně.

Pokud navrhujete strukturu dataLayer a ještě jste neslyšeli o recursive merge, zvažte, jestli chcete číst dále. Bude se vám spát hůř.

dataLayer vs. datový model GTM — to není totéž

Než se pustíme do příkladů, potřebujeme si ujasnit jednu zásadní věc. V diskuzích o dataLayer se běžně zaměňují dva pojmy, které znamenají něco jiného:

  • dataLayer — javascriptové pole (Array), do kterého pushujete objekty. Žije v prohlížeči, můžete se na něj podívat v konzoli.
  • Datový model GTM (interní stav) — objekt, který si GTM udržuje interně. Když v GTM vytvoříte proměnnou typu Data Layer Variable, čtete z tohoto interního modelu — ne přímo z pole dataLayer.

A právě tady začíná zábava. Když zavoláte dataLayer.push(), GTM vezme váš objekt a mergne ho do svého interního datového modelu. A ten merge není prostý přepis. Je to recursive merge.

Co je recursive merge

Recursive merge (hloubkové slučování) znamená, že GTM prochází strukturu objektu úroveň po úrovni a slučuje hodnoty. Primitivní hodnoty (string, number, boolean) se přepíšou. Ale vnořené objekty se neslučují jako celek — GTM sestoupí dovnitř a sloučí jednotlivé klíče.

Zní to nevinně. Podívejme se, co to znamená v praxi.

dataLayer etudy

Připravil jsem několik scénářů. U každého zkuste nejdřív odpovědět sami — a pak si přečtěte řešení.

Etuda 1: Jednoduchý přepis

Co dostanu v datovém modelu v proměnné x?

dataLayer.push({'x': 1});
dataLayer.push({'x': 2});

x = 2

Žádné překvapení. Primitivní hodnota se prostě přepíše. Tohle funguje přesně tak, jak čekáte.

Etuda 2: Vnořený objekt

Co dostanu v datovém modelu v proměnné x?

dataLayer.push({'x': {'a': 1} });
dataLayer.push({'x': {'b': 2} });

x = {'a': 1, 'b': 2}

A tady to začíná. Druhý push nepřepsal celý objekt x. GTM sestoupil dovnitř a přidal klíč b k existujícímu klíči a. Výsledek je sloučený objekt.

Pokud jste čekali {'b': 2}, nejste sami. Ale recursive merge funguje jinak — prochází strukturu a slučuje, nepřepisuje.

Etuda 3: Pole (Array)

Co dostanu v datovém modelu v proměnné x?

dataLayer.push({'x': [1, 2, 3] });
dataLayer.push({'x': [4, 5] });

x = [4, 5, 3]

Zábava začíná. Pole se v recursive merge chová jako objekt s číselnými klíči. Index 0 se přepíše na 4, index 1 na 5 a index 2 zůstane 3, protože druhý push neměl třetí prvek.

Výsledek není [4, 5] (přepis) ani [1, 2, 3, 4, 5] (concat). Je to hybrid, který nedává smysl v žádném běžném programátorském kontextu.

Tak bacha na to.

Etuda 4: Pole produktů

Co dostanu v datovém modelu v proměnné x?

dataLayer.push({'x': [{'id': 1, 'name': 'Produkt 1'}] });
dataLayer.push({'x': [{'id': 2}] });

x = [{'id': 2, 'name': 'Produkt 1'}]

Tohle je nejpraktičtější etuda. Recursive merge sestoupil do pole (index 0), uvnitř našel objekt — a mergoval ho znovu. Výsledek: produkt s id: 2 zdědil name: 'Produkt 1' z předchozího pushe. Vznikl frankenstein — objekt, který popisuje neexistující produkt.

V e-commerce měření je tohle noční můra. Pokud pushujete eventy bez čištění datového modelu, můžete v Google Analytics 4 (GA4) reportech vidět produkt s cenou jiného produktu nebo s názvem předchozí položky v košíku. A protože to nespadne s chybou, můžete na to přijít až za týdny — z dat, která nedávají smysl.

Etuda 5: Objekt + _clear

Co dostanu v datovém modelu v proměnné x?

dataLayer.push({'x': {'a': 1} });
dataLayer.push({'x': {'b': 2}, _clear: true });

x = {'b': 2}

Příznak _clear: true říká GTM: klíče v tomto pushe zapiš jako celek, nedělej recursive merge. Takže x se nepřidává k existujícímu {'a': 1} — přepíše se celý na {'b': 2}. Klíč a je pryč.

Etuda 6: _clear a jiný klíč

Co dostanu v datovém modelu v proměnné x?

dataLayer.push({'x': {'a': 1} });
dataLayer.push({'y': {'b': 2}, _clear: true });

x = {'a': 1}

_clear: true resetuje jen root klíče přítomné v daném pushe. Druhý push zapisuje pouze y — na x nesahá. Hodnota x zůstává {'a': 1}.

Tohle je důležité pochopit: _clear není globální reset celého datového modelu. Resetuje jen to, co pushujete.

Etuda 7: Pole + _clear

Co dostanu v datovém modelu v proměnné x?

dataLayer.push({'x': [1, 2, 3] });
dataLayer.push({'x': [4, 5], _clear: true });

x = [4, 5]

Se _clear: true se pole chová předvídatelně — klíč x se přepíše jako celek. Žádné hybridní slučování indexů.

Co z toho vyplývá

Data se v datovém modelu kumulují

Pokud pushnu e-commerce data pro produkt A a pak pushnu data pro produkt B, v datovém modelu zůstávají oba. Vnořené objekty se slučují, ne přepisují.

S _clear:true máte možnost ale jednotlivé části dataLayer měnit.

Vědecká poznámka

Je to jako Théseuova loď — pokud každým pushem nahradíte část objektu, je to pořád ten samý objekt?

Mmm…

Záleží na tom, kdy přesně jaká data čtete

Na single page aplikacích je to naprosto zásadní problém. Uživatel přejde z produktové stránky na kategorii — ale data o produktu jsou pořád v datovém modelu. Pokud tag čte proměnnou v moment, kdy už by tam neměla být, posíláte do GA4 nesmysly.

Na klasickém webu je tohle menší problém (stránka se reloadne, dataLayer se vytvoří znovu), ale pořád to dokáže uškodit. Typický příklad: uživatel přidá produkt do košíku, pak ho odebere. Pokud odebrání pushnete jako nový objekt bez vyčištění, data o revenue v GA4 nebudou sedět — protože v datovém modelu zůstávají zbytky předchozích pushů.

Řešení existují, ale žádné není univerzální

Tady není silver bullet. Každý přístup má trade-offy a je třeba promyslet, aby celé řešení dávalo smysl v kontextu vašeho webu:

  1. Čištění na straně vývojářů — Programátoři explicitně resetují relevantní klíče před novým pushem. Spolehlivé, ale vyžaduje disciplínu a dokumentaci. Jakékoli opomenutí = tichá chyba v datech.
  2. Selektivní čtení v GTM — Nečtete proměnnou „obecně“, ale vážete ji na konkrétní event. Trigger se aktivuje jen na ten správný push, takže čtete data v moment, kdy jsou aktuální. Funguje dobře, ale konfigurace se může zkomplikovat.
  3. _clear: true — Resetuje root klíče v daném pushe — místo recursive merge je přepíše celé. Bezpečné proti kumulaci v rámci jednoho klíče. Ale pozor: neresetuje ostatní root klíče. Pokud potřebujete vyčistit i jiné klíče, musíte je v pushe explicitně nastavit na null nebo je do pushe zahrnout.
  4. dataLayer picker – šablona, která vám z dataLayer načte pouze data z pushe, který triggeroval event. Více o dataLayer picker psal Simo Ahava.
    Pozn.: díky Markovi Leciánovi za doplnění téhle možnosti
  5. Kombinace — V praxi většinou skončíte u kombinace přístupů. Na mých projektech typicky doporučuji _clear: true pro e-commerce eventy (add_to_cart, purchase) a selektivní čtení pro ostatní.

Extra bacha na pole

Etuda 3 ukazuje, proč jsou pole v dataLayer zrádná. Pokud pushujete pole (třeba seznam produktů v košíku), recursive merge je může rozbít způsobem, který je těžké debugovat. Doporučuju:

  • Pole vždy pushovat s _clear: true, nebo
  • wrappovat je do nového objektu, aby se přepsaly jako celek, nebo
  • používat pro každý typ události vždy vlastní tag v GTM.

Jak to souvisí s kvalitou dat

Recursive merge je jeden z mechanismů, kterým musíte velmi dobře rozumět, abyste měřili správná data. Pokud ho neznáte, v GA4 reportech pak vidíte podivné hodnoty — revenue se nesčítá, produkty se duplikují, eventy nesou data z předchozích interakcí. Přesně tento typ chyb jsem prezentoval na MeasureCamp — tiché, bez erroru, ale s reálným dopadem na data.

Pokud řešíte kvalitu dat a monitoring měření, tohle je přesně typ problému, který byste měli mít na radaru. Není to chyba v kódu, která spadne s errorem. Je to chyba v datech, která potichu zkresluje vaše rozhodování.

A pokud vaše firma plánuje využívat AI nad analytickými daty, potřebujete datovou infrastrukturu, která je na to připravená. Recursive merge problém v dataLayer je přesně ten typ „drobnosti“, která dělá rozdíl mezi daty, nad kterými jde trénovat model, a daty, která ho naučí blbosti.

Shrnutí

  • dataLayer pole a datový model GTM jsou dvě různé věci.
  • GTM dělá recursive merge — vnořené objekty se slučují, ne přepisují.
  • Pole se mergují po indexech, což vede k nečekaným výsledkům.
  • _clear: true resetuje root klíče v daném pushe — zabraňuje recursive merge, ale nesahá na ostatní klíče.
  • Na SPA je tohle kritické. Na klasickém webu to bolí u událostí a e-commerce událostí.
  • Promyslete si architekturu dataLayer předem — opravovat to zpětně je drahé.

Pokud si nejste jistí, jestli se vám recursive merge problém netýká, ozvěte se mi — projdeme vaši dataLayer architekturu.