[{"data":1,"prerenderedAt":3134},["ShallowReactive",2],{"article-alternates":3,"article-\u002Fen\u002Fdata\u002Fdbt-bigquery-modern-marketing-data-stack":13},{"i18nKey":4,"paths":5},"data-002-2026-06",{"de":6,"en":7,"es":8,"fr":9,"it":10,"ru":11,"tr":12},"\u002Fde\u002Fdata\u002Fdbt-bigquery-moderner-marketing-data-stack","\u002Fen\u002Fdata\u002Fdbt-bigquery-modern-marketing-data-stack","\u002Fes\u002Fdata\u002Fdbt-bigquery-modern-marketing-data-stack","\u002Ffr\u002Fdata\u002Fdbt-bigquery-modern-marketing-data-stack","\u002Fit\u002Fdata\u002Fdbt-bigquery-modern-pazarlama-data-stack","\u002Fru\u002Fdata\u002Fdbt-bigquery-sovremenniy-marketing-data-stack","\u002Ftr\u002Fdata\u002Fdbt-bigquery-ile-modern-pazarlama-data-stack",{"_path":7,"_dir":14,"_draft":15,"_partial":15,"_locale":16,"title":17,"description":18,"publishedAt":19,"modifiedAt":19,"category":14,"i18nKey":4,"tags":20,"readingTime":26,"author":27,"body":28,"_type":3128,"_id":3129,"_source":3130,"_file":3131,"_stem":3132,"_extension":3133},"data",false,"","Modern Marketing Data Stack with dbt + BigQuery","From source mapping to semantic layer: how to turn marketing data into a decision engine. dbt modeling layers, exposure definitions, and production pipeline architecture.","2026-06-14",[21,22,23,24,25],"dbt","bigquery","data-modeling","semantic-layer","marketing-analytics",8,"Roibase",{"type":29,"children":30,"toc":3118},"root",[31,39,46,84,350,379,400,406,418,701,714,1043,1102,1108,1120,1483,1488,1520,1526,1546,1750,1770,1775,1882,1887,1893,1906,2183,2196,2209,2471,2490,2496,2501,2754,2759,2779,2953,2958,2964,2969,2974,2979,3082,3087,3093,3112],{"type":32,"tag":33,"props":34,"children":35},"element","p",{},[36],{"type":37,"value":38},"text","In 2026, marketing teams don't wrestle with data—they make decisions from it. GA4, Meta Ads, Google Ads, CRM, CDP, server-side GTM—each dumps into its own table. The team manually stitches data in spreadsheets, numbers shift every week, nobody trusts the source. This chaos vanishes with a modern data stack: BigQuery as the warehouse, dbt as the transformation layer, semantic layer as the metric graph. You version control your code, test every change, and metrics flow from a single source of truth. This article shows how dbt + BigQuery turns a marketing data pipeline into production-grade infrastructure.",{"type":32,"tag":40,"props":41,"children":43},"h2",{"id":42},"source-mapping-standardizing-raw-data-paths",[44],{"type":37,"value":45},"Source mapping: Standardizing raw data paths",{"type":32,"tag":33,"props":47,"children":48},{},[49,51,58,60,66,68,74,76,82],{"type":37,"value":50},"dbt's first job is source mapping—fitting raw data from different systems into the same schema. In BigQuery, ",{"type":32,"tag":52,"props":53,"children":55},"code",{"className":54},[],[56],{"type":37,"value":57},"analytics_123456.events_*",{"type":37,"value":59}," comes from GA4, ",{"type":32,"tag":52,"props":61,"children":63},{"className":62},[],[64],{"type":37,"value":65},"facebook_ads.ads_insights",{"type":37,"value":67}," from the Meta API, ",{"type":32,"tag":52,"props":69,"children":71},{"className":70},[],[72],{"type":37,"value":73},"crm.transactions",{"type":37,"value":75}," from Shopify. Each has a different timestamp format, different user identifier, different currency column. You define these raw tables in dbt's ",{"type":32,"tag":52,"props":77,"children":79},{"className":78},[],[80],{"type":37,"value":81},"sources.yml",{"type":37,"value":83},":",{"type":32,"tag":85,"props":86,"children":90},"pre",{"className":87,"code":88,"language":89,"meta":16,"style":16},"language-yaml shiki shiki-themes github-dark","version: 2\nsources:\n  - name: ga4\n    database: analytics_123456\n    tables:\n      - name: events_\n        identifier: \"events_*\"\n        loaded_at_field: event_timestamp\n  - name: meta_ads\n    database: facebook_ads\n    schema: public\n    tables:\n      - name: ads_insights\n        loaded_at_field: date_start\n","yaml",[91],{"type":32,"tag":52,"props":92,"children":93},{"__ignoreMap":16},[94,118,132,156,174,187,209,227,244,265,282,300,312,333],{"type":32,"tag":95,"props":96,"children":99},"span",{"class":97,"line":98},"line",1,[100,106,112],{"type":32,"tag":95,"props":101,"children":103},{"style":102},"--shiki-default:#85E89D",[104],{"type":37,"value":105},"version",{"type":32,"tag":95,"props":107,"children":109},{"style":108},"--shiki-default:#E1E4E8",[110],{"type":37,"value":111},": ",{"type":32,"tag":95,"props":113,"children":115},{"style":114},"--shiki-default:#79B8FF",[116],{"type":37,"value":117},"2\n",{"type":32,"tag":95,"props":119,"children":121},{"class":97,"line":120},2,[122,127],{"type":32,"tag":95,"props":123,"children":124},{"style":102},[125],{"type":37,"value":126},"sources",{"type":32,"tag":95,"props":128,"children":129},{"style":108},[130],{"type":37,"value":131},":\n",{"type":32,"tag":95,"props":133,"children":135},{"class":97,"line":134},3,[136,141,146,150],{"type":32,"tag":95,"props":137,"children":138},{"style":108},[139],{"type":37,"value":140},"  - ",{"type":32,"tag":95,"props":142,"children":143},{"style":102},[144],{"type":37,"value":145},"name",{"type":32,"tag":95,"props":147,"children":148},{"style":108},[149],{"type":37,"value":111},{"type":32,"tag":95,"props":151,"children":153},{"style":152},"--shiki-default:#9ECBFF",[154],{"type":37,"value":155},"ga4\n",{"type":32,"tag":95,"props":157,"children":159},{"class":97,"line":158},4,[160,165,169],{"type":32,"tag":95,"props":161,"children":162},{"style":102},[163],{"type":37,"value":164},"    database",{"type":32,"tag":95,"props":166,"children":167},{"style":108},[168],{"type":37,"value":111},{"type":32,"tag":95,"props":170,"children":171},{"style":152},[172],{"type":37,"value":173},"analytics_123456\n",{"type":32,"tag":95,"props":175,"children":177},{"class":97,"line":176},5,[178,183],{"type":32,"tag":95,"props":179,"children":180},{"style":102},[181],{"type":37,"value":182},"    tables",{"type":32,"tag":95,"props":184,"children":185},{"style":108},[186],{"type":37,"value":131},{"type":32,"tag":95,"props":188,"children":190},{"class":97,"line":189},6,[191,196,200,204],{"type":32,"tag":95,"props":192,"children":193},{"style":108},[194],{"type":37,"value":195},"      - ",{"type":32,"tag":95,"props":197,"children":198},{"style":102},[199],{"type":37,"value":145},{"type":32,"tag":95,"props":201,"children":202},{"style":108},[203],{"type":37,"value":111},{"type":32,"tag":95,"props":205,"children":206},{"style":152},[207],{"type":37,"value":208},"events_\n",{"type":32,"tag":95,"props":210,"children":212},{"class":97,"line":211},7,[213,218,222],{"type":32,"tag":95,"props":214,"children":215},{"style":102},[216],{"type":37,"value":217},"        identifier",{"type":32,"tag":95,"props":219,"children":220},{"style":108},[221],{"type":37,"value":111},{"type":32,"tag":95,"props":223,"children":224},{"style":152},[225],{"type":37,"value":226},"\"events_*\"\n",{"type":32,"tag":95,"props":228,"children":229},{"class":97,"line":26},[230,235,239],{"type":32,"tag":95,"props":231,"children":232},{"style":102},[233],{"type":37,"value":234},"        loaded_at_field",{"type":32,"tag":95,"props":236,"children":237},{"style":108},[238],{"type":37,"value":111},{"type":32,"tag":95,"props":240,"children":241},{"style":152},[242],{"type":37,"value":243},"event_timestamp\n",{"type":32,"tag":95,"props":245,"children":247},{"class":97,"line":246},9,[248,252,256,260],{"type":32,"tag":95,"props":249,"children":250},{"style":108},[251],{"type":37,"value":140},{"type":32,"tag":95,"props":253,"children":254},{"style":102},[255],{"type":37,"value":145},{"type":32,"tag":95,"props":257,"children":258},{"style":108},[259],{"type":37,"value":111},{"type":32,"tag":95,"props":261,"children":262},{"style":152},[263],{"type":37,"value":264},"meta_ads\n",{"type":32,"tag":95,"props":266,"children":268},{"class":97,"line":267},10,[269,273,277],{"type":32,"tag":95,"props":270,"children":271},{"style":102},[272],{"type":37,"value":164},{"type":32,"tag":95,"props":274,"children":275},{"style":108},[276],{"type":37,"value":111},{"type":32,"tag":95,"props":278,"children":279},{"style":152},[280],{"type":37,"value":281},"facebook_ads\n",{"type":32,"tag":95,"props":283,"children":285},{"class":97,"line":284},11,[286,291,295],{"type":32,"tag":95,"props":287,"children":288},{"style":102},[289],{"type":37,"value":290},"    schema",{"type":32,"tag":95,"props":292,"children":293},{"style":108},[294],{"type":37,"value":111},{"type":32,"tag":95,"props":296,"children":297},{"style":152},[298],{"type":37,"value":299},"public\n",{"type":32,"tag":95,"props":301,"children":303},{"class":97,"line":302},12,[304,308],{"type":32,"tag":95,"props":305,"children":306},{"style":102},[307],{"type":37,"value":182},{"type":32,"tag":95,"props":309,"children":310},{"style":108},[311],{"type":37,"value":131},{"type":32,"tag":95,"props":313,"children":315},{"class":97,"line":314},13,[316,320,324,328],{"type":32,"tag":95,"props":317,"children":318},{"style":108},[319],{"type":37,"value":195},{"type":32,"tag":95,"props":321,"children":322},{"style":102},[323],{"type":37,"value":145},{"type":32,"tag":95,"props":325,"children":326},{"style":108},[327],{"type":37,"value":111},{"type":32,"tag":95,"props":329,"children":330},{"style":152},[331],{"type":37,"value":332},"ads_insights\n",{"type":32,"tag":95,"props":334,"children":336},{"class":97,"line":335},14,[337,341,345],{"type":32,"tag":95,"props":338,"children":339},{"style":102},[340],{"type":37,"value":234},{"type":32,"tag":95,"props":342,"children":343},{"style":108},[344],{"type":37,"value":111},{"type":32,"tag":95,"props":346,"children":347},{"style":152},[348],{"type":37,"value":349},"date_start\n",{"type":32,"tag":33,"props":351,"children":352},{},[353,355,361,363,369,371,377],{"type":37,"value":354},"This definition tells dbt: \"These are upstream sources, I don't own them, but I'll test their freshness.\" Running ",{"type":32,"tag":52,"props":356,"children":358},{"className":357},[],[359],{"type":37,"value":360},"dbt source freshness",{"type":37,"value":362}," checks when the last data arrived—if Meta API is delayed, an alert fires. Without source mapping, every model writes ",{"type":32,"tag":52,"props":364,"children":366},{"className":365},[],[367],{"type":37,"value":368},"SELECT * FROM analytics_123456.events_20260614",{"type":37,"value":370}," directly; when the table name changes, 40 models break. With mapping, the reference becomes ",{"type":32,"tag":52,"props":372,"children":374},{"className":373},[],[375],{"type":37,"value":376},"{{ source('ga4', 'events_') }}",{"type":37,"value":378},", and changes propagate from a single point.",{"type":32,"tag":33,"props":380,"children":381},{},[382,384,390,392,398],{"type":37,"value":383},"GA4 event_timestamp is Unix microseconds, Meta ads date_start is an ISO string, CRM created_at is UTC datetime—all different formats. In source mapping, you extract a standard timestamp column: ",{"type":32,"tag":52,"props":385,"children":387},{"className":386},[],[388],{"type":37,"value":389},"TIMESTAMP_MICROS(event_timestamp) AS event_time",{"type":37,"value":391}," for GA4, ",{"type":32,"tag":52,"props":393,"children":395},{"className":394},[],[396],{"type":37,"value":397},"PARSE_TIMESTAMP('%Y-%m-%d', date_start) AS event_time",{"type":37,"value":399}," for Meta. This normalization feeds clean input downstream.",{"type":32,"tag":40,"props":401,"children":403},{"id":402},"modeling-layer-staging-intermediate-mart",[404],{"type":37,"value":405},"Modeling layer: Staging, intermediate, mart",{"type":32,"tag":33,"props":407,"children":408},{},[409,411,417],{"type":37,"value":410},"dbt's power lies in layered modeling—staging, intermediate, and mart tiers. Staging models pull 1:1 from sources, only renaming and casting types. ",{"type":32,"tag":52,"props":412,"children":414},{"className":413},[],[415],{"type":37,"value":416},"stg_ga4_events.sql",{"type":37,"value":83},{"type":32,"tag":85,"props":419,"children":423},{"className":420,"code":421,"language":422,"meta":16,"style":16},"language-sql shiki shiki-themes github-dark","SELECT\n  TIMESTAMP_MICROS(event_timestamp) AS event_time,\n  user_pseudo_id AS anonymous_id,\n  event_name,\n  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'session_id') AS session_id,\n  geo.country,\n  device.category AS device_category\nFROM {{ source('ga4', 'events_') }}\nWHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))\n  AND FORMAT_DATE('%Y%m%d', CURRENT_DATE())\n","sql",[424],{"type":32,"tag":52,"props":425,"children":426},{"__ignoreMap":16},[427,436,454,471,479,551,573,600,633,680],{"type":32,"tag":95,"props":428,"children":429},{"class":97,"line":98},[430],{"type":32,"tag":95,"props":431,"children":433},{"style":432},"--shiki-default:#F97583",[434],{"type":37,"value":435},"SELECT\n",{"type":32,"tag":95,"props":437,"children":438},{"class":97,"line":120},[439,444,449],{"type":32,"tag":95,"props":440,"children":441},{"style":108},[442],{"type":37,"value":443},"  TIMESTAMP_MICROS(event_timestamp) ",{"type":32,"tag":95,"props":445,"children":446},{"style":432},[447],{"type":37,"value":448},"AS",{"type":32,"tag":95,"props":450,"children":451},{"style":108},[452],{"type":37,"value":453}," event_time,\n",{"type":32,"tag":95,"props":455,"children":456},{"class":97,"line":134},[457,462,466],{"type":32,"tag":95,"props":458,"children":459},{"style":108},[460],{"type":37,"value":461},"  user_pseudo_id ",{"type":32,"tag":95,"props":463,"children":464},{"style":432},[465],{"type":37,"value":448},{"type":32,"tag":95,"props":467,"children":468},{"style":108},[469],{"type":37,"value":470}," anonymous_id,\n",{"type":32,"tag":95,"props":472,"children":473},{"class":97,"line":158},[474],{"type":32,"tag":95,"props":475,"children":476},{"style":108},[477],{"type":37,"value":478},"  event_name,\n",{"type":32,"tag":95,"props":480,"children":481},{"class":97,"line":176},[482,487,492,497,502,507,512,517,522,527,532,537,542,546],{"type":32,"tag":95,"props":483,"children":484},{"style":108},[485],{"type":37,"value":486},"  (",{"type":32,"tag":95,"props":488,"children":489},{"style":432},[490],{"type":37,"value":491},"SELECT",{"type":32,"tag":95,"props":493,"children":494},{"style":114},[495],{"type":37,"value":496}," value",{"type":32,"tag":95,"props":498,"children":499},{"style":108},[500],{"type":37,"value":501},".",{"type":32,"tag":95,"props":503,"children":504},{"style":114},[505],{"type":37,"value":506},"string_value",{"type":32,"tag":95,"props":508,"children":509},{"style":432},[510],{"type":37,"value":511}," FROM",{"type":32,"tag":95,"props":513,"children":514},{"style":108},[515],{"type":37,"value":516}," UNNEST(event_params) ",{"type":32,"tag":95,"props":518,"children":519},{"style":432},[520],{"type":37,"value":521},"WHERE",{"type":32,"tag":95,"props":523,"children":524},{"style":432},[525],{"type":37,"value":526}," key",{"type":32,"tag":95,"props":528,"children":529},{"style":432},[530],{"type":37,"value":531}," =",{"type":32,"tag":95,"props":533,"children":534},{"style":152},[535],{"type":37,"value":536}," 'session_id'",{"type":32,"tag":95,"props":538,"children":539},{"style":108},[540],{"type":37,"value":541},") ",{"type":32,"tag":95,"props":543,"children":544},{"style":432},[545],{"type":37,"value":448},{"type":32,"tag":95,"props":547,"children":548},{"style":108},[549],{"type":37,"value":550}," session_id,\n",{"type":32,"tag":95,"props":552,"children":553},{"class":97,"line":189},[554,559,563,568],{"type":32,"tag":95,"props":555,"children":556},{"style":114},[557],{"type":37,"value":558},"  geo",{"type":32,"tag":95,"props":560,"children":561},{"style":108},[562],{"type":37,"value":501},{"type":32,"tag":95,"props":564,"children":565},{"style":114},[566],{"type":37,"value":567},"country",{"type":32,"tag":95,"props":569,"children":570},{"style":108},[571],{"type":37,"value":572},",\n",{"type":32,"tag":95,"props":574,"children":575},{"class":97,"line":211},[576,581,585,590,595],{"type":32,"tag":95,"props":577,"children":578},{"style":114},[579],{"type":37,"value":580},"  device",{"type":32,"tag":95,"props":582,"children":583},{"style":108},[584],{"type":37,"value":501},{"type":32,"tag":95,"props":586,"children":587},{"style":114},[588],{"type":37,"value":589},"category",{"type":32,"tag":95,"props":591,"children":592},{"style":432},[593],{"type":37,"value":594}," AS",{"type":32,"tag":95,"props":596,"children":597},{"style":108},[598],{"type":37,"value":599}," device_category\n",{"type":32,"tag":95,"props":601,"children":602},{"class":97,"line":26},[603,608,613,618,623,628],{"type":32,"tag":95,"props":604,"children":605},{"style":432},[606],{"type":37,"value":607},"FROM",{"type":32,"tag":95,"props":609,"children":610},{"style":108},[611],{"type":37,"value":612}," {{ source(",{"type":32,"tag":95,"props":614,"children":615},{"style":152},[616],{"type":37,"value":617},"'ga4'",{"type":32,"tag":95,"props":619,"children":620},{"style":108},[621],{"type":37,"value":622},", ",{"type":32,"tag":95,"props":624,"children":625},{"style":152},[626],{"type":37,"value":627},"'events_'",{"type":32,"tag":95,"props":629,"children":630},{"style":108},[631],{"type":37,"value":632},") }}\n",{"type":32,"tag":95,"props":634,"children":635},{"class":97,"line":246},[636,640,645,650,655,660,665,670,675],{"type":32,"tag":95,"props":637,"children":638},{"style":432},[639],{"type":37,"value":521},{"type":32,"tag":95,"props":641,"children":642},{"style":108},[643],{"type":37,"value":644}," _TABLE_SUFFIX ",{"type":32,"tag":95,"props":646,"children":647},{"style":432},[648],{"type":37,"value":649},"BETWEEN",{"type":32,"tag":95,"props":651,"children":652},{"style":108},[653],{"type":37,"value":654}," FORMAT_DATE(",{"type":32,"tag":95,"props":656,"children":657},{"style":152},[658],{"type":37,"value":659},"'%Y%m%d'",{"type":32,"tag":95,"props":661,"children":662},{"style":108},[663],{"type":37,"value":664},", DATE_SUB(CURRENT_DATE(), INTERVAL ",{"type":32,"tag":95,"props":666,"children":667},{"style":114},[668],{"type":37,"value":669},"90",{"type":32,"tag":95,"props":671,"children":672},{"style":432},[673],{"type":37,"value":674}," DAY",{"type":32,"tag":95,"props":676,"children":677},{"style":108},[678],{"type":37,"value":679},"))\n",{"type":32,"tag":95,"props":681,"children":682},{"class":97,"line":267},[683,688,692,696],{"type":32,"tag":95,"props":684,"children":685},{"style":432},[686],{"type":37,"value":687},"  AND",{"type":32,"tag":95,"props":689,"children":690},{"style":108},[691],{"type":37,"value":654},{"type":32,"tag":95,"props":693,"children":694},{"style":152},[695],{"type":37,"value":659},{"type":32,"tag":95,"props":697,"children":698},{"style":108},[699],{"type":37,"value":700},", CURRENT_DATE())\n",{"type":32,"tag":33,"props":702,"children":703},{},[704,706,712],{"type":37,"value":705},"Staging delivers clean data with no business logic. Intermediate models layer in business logic: sessionization, attribution, funnel steps. ",{"type":32,"tag":52,"props":707,"children":709},{"className":708},[],[710],{"type":37,"value":711},"int_sessions.sql",{"type":37,"value":713}," aggregates GA4 events to session grain:",{"type":32,"tag":85,"props":715,"children":717},{"className":420,"code":716,"language":422,"meta":16,"style":16},"WITH session_events AS (\n  SELECT\n    session_id,\n    MIN(event_time) AS session_start,\n    MAX(event_time) AS session_end,\n    COUNT(DISTINCT CASE WHEN event_name = 'page_view' THEN event_time END) AS pageviews,\n    MAX(CASE WHEN event_name = 'purchase' THEN 1 ELSE 0 END) AS converted\n  FROM {{ ref('stg_ga4_events') }}\n  GROUP BY session_id\n)\nSELECT\n  *,\n  TIMESTAMP_DIFF(session_end, session_start, SECOND) AS duration_seconds\nFROM session_events\n",[718],{"type":32,"tag":52,"props":719,"children":720},{"__ignoreMap":16},[721,743,751,759,781,802,873,943,965,978,986,993,1005,1031],{"type":32,"tag":95,"props":722,"children":723},{"class":97,"line":98},[724,729,734,738],{"type":32,"tag":95,"props":725,"children":726},{"style":432},[727],{"type":37,"value":728},"WITH",{"type":32,"tag":95,"props":730,"children":731},{"style":108},[732],{"type":37,"value":733}," session_events ",{"type":32,"tag":95,"props":735,"children":736},{"style":432},[737],{"type":37,"value":448},{"type":32,"tag":95,"props":739,"children":740},{"style":108},[741],{"type":37,"value":742}," (\n",{"type":32,"tag":95,"props":744,"children":745},{"class":97,"line":120},[746],{"type":32,"tag":95,"props":747,"children":748},{"style":432},[749],{"type":37,"value":750},"  SELECT\n",{"type":32,"tag":95,"props":752,"children":753},{"class":97,"line":134},[754],{"type":32,"tag":95,"props":755,"children":756},{"style":108},[757],{"type":37,"value":758},"    session_id,\n",{"type":32,"tag":95,"props":760,"children":761},{"class":97,"line":158},[762,767,772,776],{"type":32,"tag":95,"props":763,"children":764},{"style":114},[765],{"type":37,"value":766},"    MIN",{"type":32,"tag":95,"props":768,"children":769},{"style":108},[770],{"type":37,"value":771},"(event_time) ",{"type":32,"tag":95,"props":773,"children":774},{"style":432},[775],{"type":37,"value":448},{"type":32,"tag":95,"props":777,"children":778},{"style":108},[779],{"type":37,"value":780}," session_start,\n",{"type":32,"tag":95,"props":782,"children":783},{"class":97,"line":176},[784,789,793,797],{"type":32,"tag":95,"props":785,"children":786},{"style":114},[787],{"type":37,"value":788},"    MAX",{"type":32,"tag":95,"props":790,"children":791},{"style":108},[792],{"type":37,"value":771},{"type":32,"tag":95,"props":794,"children":795},{"style":432},[796],{"type":37,"value":448},{"type":32,"tag":95,"props":798,"children":799},{"style":108},[800],{"type":37,"value":801}," session_end,\n",{"type":32,"tag":95,"props":803,"children":804},{"class":97,"line":189},[805,810,815,820,825,830,835,840,845,850,855,860,864,868],{"type":32,"tag":95,"props":806,"children":807},{"style":114},[808],{"type":37,"value":809},"    COUNT",{"type":32,"tag":95,"props":811,"children":812},{"style":108},[813],{"type":37,"value":814},"(",{"type":32,"tag":95,"props":816,"children":817},{"style":432},[818],{"type":37,"value":819},"DISTINCT",{"type":32,"tag":95,"props":821,"children":822},{"style":432},[823],{"type":37,"value":824}," CASE",{"type":32,"tag":95,"props":826,"children":827},{"style":432},[828],{"type":37,"value":829}," WHEN",{"type":32,"tag":95,"props":831,"children":832},{"style":108},[833],{"type":37,"value":834}," event_name ",{"type":32,"tag":95,"props":836,"children":837},{"style":432},[838],{"type":37,"value":839},"=",{"type":32,"tag":95,"props":841,"children":842},{"style":152},[843],{"type":37,"value":844}," 'page_view'",{"type":32,"tag":95,"props":846,"children":847},{"style":432},[848],{"type":37,"value":849}," THEN",{"type":32,"tag":95,"props":851,"children":852},{"style":108},[853],{"type":37,"value":854}," event_time ",{"type":32,"tag":95,"props":856,"children":857},{"style":432},[858],{"type":37,"value":859},"END",{"type":32,"tag":95,"props":861,"children":862},{"style":108},[863],{"type":37,"value":541},{"type":32,"tag":95,"props":865,"children":866},{"style":432},[867],{"type":37,"value":448},{"type":32,"tag":95,"props":869,"children":870},{"style":108},[871],{"type":37,"value":872}," pageviews,\n",{"type":32,"tag":95,"props":874,"children":875},{"class":97,"line":211},[876,880,884,889,893,897,901,906,910,915,920,925,930,934,938],{"type":32,"tag":95,"props":877,"children":878},{"style":114},[879],{"type":37,"value":788},{"type":32,"tag":95,"props":881,"children":882},{"style":108},[883],{"type":37,"value":814},{"type":32,"tag":95,"props":885,"children":886},{"style":432},[887],{"type":37,"value":888},"CASE",{"type":32,"tag":95,"props":890,"children":891},{"style":432},[892],{"type":37,"value":829},{"type":32,"tag":95,"props":894,"children":895},{"style":108},[896],{"type":37,"value":834},{"type":32,"tag":95,"props":898,"children":899},{"style":432},[900],{"type":37,"value":839},{"type":32,"tag":95,"props":902,"children":903},{"style":152},[904],{"type":37,"value":905}," 'purchase'",{"type":32,"tag":95,"props":907,"children":908},{"style":432},[909],{"type":37,"value":849},{"type":32,"tag":95,"props":911,"children":912},{"style":114},[913],{"type":37,"value":914}," 1",{"type":32,"tag":95,"props":916,"children":917},{"style":432},[918],{"type":37,"value":919}," ELSE",{"type":32,"tag":95,"props":921,"children":922},{"style":114},[923],{"type":37,"value":924}," 0",{"type":32,"tag":95,"props":926,"children":927},{"style":432},[928],{"type":37,"value":929}," END",{"type":32,"tag":95,"props":931,"children":932},{"style":108},[933],{"type":37,"value":541},{"type":32,"tag":95,"props":935,"children":936},{"style":432},[937],{"type":37,"value":448},{"type":32,"tag":95,"props":939,"children":940},{"style":108},[941],{"type":37,"value":942}," converted\n",{"type":32,"tag":95,"props":944,"children":945},{"class":97,"line":26},[946,951,956,961],{"type":32,"tag":95,"props":947,"children":948},{"style":432},[949],{"type":37,"value":950},"  FROM",{"type":32,"tag":95,"props":952,"children":953},{"style":108},[954],{"type":37,"value":955}," {{ ref(",{"type":32,"tag":95,"props":957,"children":958},{"style":152},[959],{"type":37,"value":960},"'stg_ga4_events'",{"type":32,"tag":95,"props":962,"children":963},{"style":108},[964],{"type":37,"value":632},{"type":32,"tag":95,"props":966,"children":967},{"class":97,"line":246},[968,973],{"type":32,"tag":95,"props":969,"children":970},{"style":432},[971],{"type":37,"value":972},"  GROUP BY",{"type":32,"tag":95,"props":974,"children":975},{"style":108},[976],{"type":37,"value":977}," session_id\n",{"type":32,"tag":95,"props":979,"children":980},{"class":97,"line":267},[981],{"type":32,"tag":95,"props":982,"children":983},{"style":108},[984],{"type":37,"value":985},")\n",{"type":32,"tag":95,"props":987,"children":988},{"class":97,"line":284},[989],{"type":32,"tag":95,"props":990,"children":991},{"style":432},[992],{"type":37,"value":435},{"type":32,"tag":95,"props":994,"children":995},{"class":97,"line":302},[996,1001],{"type":32,"tag":95,"props":997,"children":998},{"style":432},[999],{"type":37,"value":1000},"  *",{"type":32,"tag":95,"props":1002,"children":1003},{"style":108},[1004],{"type":37,"value":572},{"type":32,"tag":95,"props":1006,"children":1007},{"class":97,"line":314},[1008,1013,1018,1022,1026],{"type":32,"tag":95,"props":1009,"children":1010},{"style":108},[1011],{"type":37,"value":1012},"  TIMESTAMP_DIFF(session_end, session_start, ",{"type":32,"tag":95,"props":1014,"children":1015},{"style":432},[1016],{"type":37,"value":1017},"SECOND",{"type":32,"tag":95,"props":1019,"children":1020},{"style":108},[1021],{"type":37,"value":541},{"type":32,"tag":95,"props":1023,"children":1024},{"style":432},[1025],{"type":37,"value":448},{"type":32,"tag":95,"props":1027,"children":1028},{"style":108},[1029],{"type":37,"value":1030}," duration_seconds\n",{"type":32,"tag":95,"props":1032,"children":1033},{"class":97,"line":335},[1034,1038],{"type":32,"tag":95,"props":1035,"children":1036},{"style":432},[1037],{"type":37,"value":607},{"type":32,"tag":95,"props":1039,"children":1040},{"style":108},[1041],{"type":37,"value":1042}," session_events\n",{"type":32,"tag":33,"props":1044,"children":1045},{},[1046,1048,1054,1056,1062,1063,1069,1070,1076,1078,1084,1086,1092,1094,1100],{"type":37,"value":1047},"Mart models are the final consumption layer—BI tools, Looker, internal dashboards query here. ",{"type":32,"tag":52,"props":1049,"children":1051},{"className":1050},[],[1052],{"type":37,"value":1053},"fct_marketing_performance.sql",{"type":37,"value":1055}," unifies all channels, calculates spend + revenue + ROAS. Each mart focuses on one business entity: ",{"type":32,"tag":52,"props":1057,"children":1059},{"className":1058},[],[1060],{"type":37,"value":1061},"dim_customers",{"type":37,"value":622},{"type":32,"tag":52,"props":1064,"children":1066},{"className":1065},[],[1067],{"type":37,"value":1068},"fct_orders",{"type":37,"value":622},{"type":32,"tag":52,"props":1071,"children":1073},{"className":1072},[],[1074],{"type":37,"value":1075},"fct_sessions",{"type":37,"value":1077},". Mart naming convention is critical—",{"type":32,"tag":52,"props":1079,"children":1081},{"className":1080},[],[1082],{"type":37,"value":1083},"dim_",{"type":37,"value":1085}," for dimensions (customer, product), ",{"type":32,"tag":52,"props":1087,"children":1089},{"className":1088},[],[1090],{"type":37,"value":1091},"fct_",{"type":37,"value":1093}," for facts (transaction, event), ",{"type":32,"tag":52,"props":1095,"children":1097},{"className":1096},[],[1098],{"type":37,"value":1099},"rpt_",{"type":37,"value":1101}," for report aggregates.",{"type":32,"tag":40,"props":1103,"children":1105},{"id":1104},"semantic-layer-kpi-definitions-as-code",[1106],{"type":37,"value":1107},"Semantic layer: KPI definitions as code",{"type":32,"tag":33,"props":1109,"children":1110},{},[1111,1113,1119],{"type":37,"value":1112},"The semantic layer pulls metric definitions into dbt—\"what is revenue,\" \"how is CAC calculated\"—no longer a spreadsheet, now YAML in your codebase. With dbt v1.6+, you build your metric graph in ",{"type":32,"tag":52,"props":1114,"children":1116},{"className":1115},[],[1117],{"type":37,"value":1118},"metrics.yml",{"type":37,"value":83},{"type":32,"tag":85,"props":1121,"children":1123},{"className":87,"code":1122,"language":89,"meta":16,"style":16},"version: 2\nmetrics:\n  - name: revenue\n    label: Revenue\n    model: ref('fct_orders')\n    calculation_method: sum\n    expression: order_amount\n    timestamp: order_date\n    time_grains: [day, week, month, quarter]\n    dimensions:\n      - channel\n      - country\n      - device_category\n\n  - name: cac\n    label: Customer Acquisition Cost\n    calculation_method: derived\n    expression: \"{{ metric('ad_spend') }} \u002F {{ metric('new_customers') }}\"\n    timestamp: acquisition_date\n    time_grains: [month, quarter]\n",[1124],{"type":32,"tag":52,"props":1125,"children":1126},{"__ignoreMap":16},[1127,1142,1154,1174,1191,1208,1225,1242,1259,1309,1321,1333,1345,1357,1366,1387,1404,1421,1438,1455],{"type":32,"tag":95,"props":1128,"children":1129},{"class":97,"line":98},[1130,1134,1138],{"type":32,"tag":95,"props":1131,"children":1132},{"style":102},[1133],{"type":37,"value":105},{"type":32,"tag":95,"props":1135,"children":1136},{"style":108},[1137],{"type":37,"value":111},{"type":32,"tag":95,"props":1139,"children":1140},{"style":114},[1141],{"type":37,"value":117},{"type":32,"tag":95,"props":1143,"children":1144},{"class":97,"line":120},[1145,1150],{"type":32,"tag":95,"props":1146,"children":1147},{"style":102},[1148],{"type":37,"value":1149},"metrics",{"type":32,"tag":95,"props":1151,"children":1152},{"style":108},[1153],{"type":37,"value":131},{"type":32,"tag":95,"props":1155,"children":1156},{"class":97,"line":134},[1157,1161,1165,1169],{"type":32,"tag":95,"props":1158,"children":1159},{"style":108},[1160],{"type":37,"value":140},{"type":32,"tag":95,"props":1162,"children":1163},{"style":102},[1164],{"type":37,"value":145},{"type":32,"tag":95,"props":1166,"children":1167},{"style":108},[1168],{"type":37,"value":111},{"type":32,"tag":95,"props":1170,"children":1171},{"style":152},[1172],{"type":37,"value":1173},"revenue\n",{"type":32,"tag":95,"props":1175,"children":1176},{"class":97,"line":158},[1177,1182,1186],{"type":32,"tag":95,"props":1178,"children":1179},{"style":102},[1180],{"type":37,"value":1181},"    label",{"type":32,"tag":95,"props":1183,"children":1184},{"style":108},[1185],{"type":37,"value":111},{"type":32,"tag":95,"props":1187,"children":1188},{"style":152},[1189],{"type":37,"value":1190},"Revenue\n",{"type":32,"tag":95,"props":1192,"children":1193},{"class":97,"line":176},[1194,1199,1203],{"type":32,"tag":95,"props":1195,"children":1196},{"style":102},[1197],{"type":37,"value":1198},"    model",{"type":32,"tag":95,"props":1200,"children":1201},{"style":108},[1202],{"type":37,"value":111},{"type":32,"tag":95,"props":1204,"children":1205},{"style":152},[1206],{"type":37,"value":1207},"ref('fct_orders')\n",{"type":32,"tag":95,"props":1209,"children":1210},{"class":97,"line":189},[1211,1216,1220],{"type":32,"tag":95,"props":1212,"children":1213},{"style":102},[1214],{"type":37,"value":1215},"    calculation_method",{"type":32,"tag":95,"props":1217,"children":1218},{"style":108},[1219],{"type":37,"value":111},{"type":32,"tag":95,"props":1221,"children":1222},{"style":152},[1223],{"type":37,"value":1224},"sum\n",{"type":32,"tag":95,"props":1226,"children":1227},{"class":97,"line":211},[1228,1233,1237],{"type":32,"tag":95,"props":1229,"children":1230},{"style":102},[1231],{"type":37,"value":1232},"    expression",{"type":32,"tag":95,"props":1234,"children":1235},{"style":108},[1236],{"type":37,"value":111},{"type":32,"tag":95,"props":1238,"children":1239},{"style":152},[1240],{"type":37,"value":1241},"order_amount\n",{"type":32,"tag":95,"props":1243,"children":1244},{"class":97,"line":26},[1245,1250,1254],{"type":32,"tag":95,"props":1246,"children":1247},{"style":102},[1248],{"type":37,"value":1249},"    timestamp",{"type":32,"tag":95,"props":1251,"children":1252},{"style":108},[1253],{"type":37,"value":111},{"type":32,"tag":95,"props":1255,"children":1256},{"style":152},[1257],{"type":37,"value":1258},"order_date\n",{"type":32,"tag":95,"props":1260,"children":1261},{"class":97,"line":246},[1262,1267,1272,1277,1281,1286,1290,1295,1299,1304],{"type":32,"tag":95,"props":1263,"children":1264},{"style":102},[1265],{"type":37,"value":1266},"    time_grains",{"type":32,"tag":95,"props":1268,"children":1269},{"style":108},[1270],{"type":37,"value":1271},": [",{"type":32,"tag":95,"props":1273,"children":1274},{"style":152},[1275],{"type":37,"value":1276},"day",{"type":32,"tag":95,"props":1278,"children":1279},{"style":108},[1280],{"type":37,"value":622},{"type":32,"tag":95,"props":1282,"children":1283},{"style":152},[1284],{"type":37,"value":1285},"week",{"type":32,"tag":95,"props":1287,"children":1288},{"style":108},[1289],{"type":37,"value":622},{"type":32,"tag":95,"props":1291,"children":1292},{"style":152},[1293],{"type":37,"value":1294},"month",{"type":32,"tag":95,"props":1296,"children":1297},{"style":108},[1298],{"type":37,"value":622},{"type":32,"tag":95,"props":1300,"children":1301},{"style":152},[1302],{"type":37,"value":1303},"quarter",{"type":32,"tag":95,"props":1305,"children":1306},{"style":108},[1307],{"type":37,"value":1308},"]\n",{"type":32,"tag":95,"props":1310,"children":1311},{"class":97,"line":267},[1312,1317],{"type":32,"tag":95,"props":1313,"children":1314},{"style":102},[1315],{"type":37,"value":1316},"    dimensions",{"type":32,"tag":95,"props":1318,"children":1319},{"style":108},[1320],{"type":37,"value":131},{"type":32,"tag":95,"props":1322,"children":1323},{"class":97,"line":284},[1324,1328],{"type":32,"tag":95,"props":1325,"children":1326},{"style":108},[1327],{"type":37,"value":195},{"type":32,"tag":95,"props":1329,"children":1330},{"style":152},[1331],{"type":37,"value":1332},"channel\n",{"type":32,"tag":95,"props":1334,"children":1335},{"class":97,"line":302},[1336,1340],{"type":32,"tag":95,"props":1337,"children":1338},{"style":108},[1339],{"type":37,"value":195},{"type":32,"tag":95,"props":1341,"children":1342},{"style":152},[1343],{"type":37,"value":1344},"country\n",{"type":32,"tag":95,"props":1346,"children":1347},{"class":97,"line":314},[1348,1352],{"type":32,"tag":95,"props":1349,"children":1350},{"style":108},[1351],{"type":37,"value":195},{"type":32,"tag":95,"props":1353,"children":1354},{"style":152},[1355],{"type":37,"value":1356},"device_category\n",{"type":32,"tag":95,"props":1358,"children":1359},{"class":97,"line":335},[1360],{"type":32,"tag":95,"props":1361,"children":1363},{"emptyLinePlaceholder":1362},true,[1364],{"type":37,"value":1365},"\n",{"type":32,"tag":95,"props":1367,"children":1369},{"class":97,"line":1368},15,[1370,1374,1378,1382],{"type":32,"tag":95,"props":1371,"children":1372},{"style":108},[1373],{"type":37,"value":140},{"type":32,"tag":95,"props":1375,"children":1376},{"style":102},[1377],{"type":37,"value":145},{"type":32,"tag":95,"props":1379,"children":1380},{"style":108},[1381],{"type":37,"value":111},{"type":32,"tag":95,"props":1383,"children":1384},{"style":152},[1385],{"type":37,"value":1386},"cac\n",{"type":32,"tag":95,"props":1388,"children":1390},{"class":97,"line":1389},16,[1391,1395,1399],{"type":32,"tag":95,"props":1392,"children":1393},{"style":102},[1394],{"type":37,"value":1181},{"type":32,"tag":95,"props":1396,"children":1397},{"style":108},[1398],{"type":37,"value":111},{"type":32,"tag":95,"props":1400,"children":1401},{"style":152},[1402],{"type":37,"value":1403},"Customer Acquisition Cost\n",{"type":32,"tag":95,"props":1405,"children":1407},{"class":97,"line":1406},17,[1408,1412,1416],{"type":32,"tag":95,"props":1409,"children":1410},{"style":102},[1411],{"type":37,"value":1215},{"type":32,"tag":95,"props":1413,"children":1414},{"style":108},[1415],{"type":37,"value":111},{"type":32,"tag":95,"props":1417,"children":1418},{"style":152},[1419],{"type":37,"value":1420},"derived\n",{"type":32,"tag":95,"props":1422,"children":1424},{"class":97,"line":1423},18,[1425,1429,1433],{"type":32,"tag":95,"props":1426,"children":1427},{"style":102},[1428],{"type":37,"value":1232},{"type":32,"tag":95,"props":1430,"children":1431},{"style":108},[1432],{"type":37,"value":111},{"type":32,"tag":95,"props":1434,"children":1435},{"style":152},[1436],{"type":37,"value":1437},"\"{{ metric('ad_spend') }} \u002F {{ metric('new_customers') }}\"\n",{"type":32,"tag":95,"props":1439,"children":1441},{"class":97,"line":1440},19,[1442,1446,1450],{"type":32,"tag":95,"props":1443,"children":1444},{"style":102},[1445],{"type":37,"value":1249},{"type":32,"tag":95,"props":1447,"children":1448},{"style":108},[1449],{"type":37,"value":111},{"type":32,"tag":95,"props":1451,"children":1452},{"style":152},[1453],{"type":37,"value":1454},"acquisition_date\n",{"type":32,"tag":95,"props":1456,"children":1458},{"class":97,"line":1457},20,[1459,1463,1467,1471,1475,1479],{"type":32,"tag":95,"props":1460,"children":1461},{"style":102},[1462],{"type":37,"value":1266},{"type":32,"tag":95,"props":1464,"children":1465},{"style":108},[1466],{"type":37,"value":1271},{"type":32,"tag":95,"props":1468,"children":1469},{"style":152},[1470],{"type":37,"value":1294},{"type":32,"tag":95,"props":1472,"children":1473},{"style":108},[1474],{"type":37,"value":622},{"type":32,"tag":95,"props":1476,"children":1477},{"style":152},[1478],{"type":37,"value":1303},{"type":32,"tag":95,"props":1480,"children":1481},{"style":108},[1482],{"type":37,"value":1308},{"type":32,"tag":33,"props":1484,"children":1485},{},[1486],{"type":37,"value":1487},"With a semantic layer, your BI tool doesn't calculate CAC—dbt does. When Looker asks for CAC, dbt returns compiled SQL that joins spend and new customer tables, then divides. The definition is code, tracked in git history—\"who changed the CAC formula and why\" has an answer. The spreadsheet formula doesn't vanish; it has version control.",{"type":32,"tag":33,"props":1489,"children":1490},{},[1491,1493,1502,1504,1510,1512,1518],{"type":37,"value":1492},"At Roibase, semantic layer setup is part of ",{"type":32,"tag":1494,"props":1495,"children":1499},"a",{"href":1496,"rel":1497},"https:\u002F\u002Fwww.roibase.com.tr\u002Fen\u002Fverianalizi",[1498],"nofollow",[1500],{"type":37,"value":1501},"data analytics & insights engineering",{"type":37,"value":1503}," scope—not just metric definitions, but KPI tree mapping, dimension hierarchy, and grain standardization. For example, \"revenue\" is the sum of ",{"type":32,"tag":52,"props":1505,"children":1507},{"className":1506},[],[1508],{"type":37,"value":1509},"fct_orders.order_amount",{"type":37,"value":1511},", but \"recognized_revenue\" filters the same table by ",{"type":32,"tag":52,"props":1513,"children":1515},{"className":1514},[],[1516],{"type":37,"value":1517},"recognized_at",{"type":37,"value":1519}," timestamp (SaaS subscription model). One table, two metrics, different business logic.",{"type":32,"tag":40,"props":1521,"children":1523},{"id":1522},"exposures-making-downstream-dependencies-visible",[1524],{"type":37,"value":1525},"Exposures: Making downstream dependencies visible",{"type":32,"tag":33,"props":1527,"children":1528},{},[1529,1531,1537,1539,1545],{"type":37,"value":1530},"An exposure answers dbt's \"who uses this model\" question. If a Looker dashboard queries ",{"type":32,"tag":52,"props":1532,"children":1534},{"className":1533},[],[1535],{"type":37,"value":1536},"fct_marketing_performance",{"type":37,"value":1538},", you define it in ",{"type":32,"tag":52,"props":1540,"children":1542},{"className":1541},[],[1543],{"type":37,"value":1544},"exposures.yml",{"type":37,"value":83},{"type":32,"tag":85,"props":1547,"children":1549},{"className":87,"code":1548,"language":89,"meta":16,"style":16},"version: 2\nexposures:\n  - name: marketing_dashboard\n    type: dashboard\n    maturity: high\n    owner:\n      name: Growth Team\n      email: growth@company.com\n    depends_on:\n      - ref('fct_marketing_performance')\n      - ref('dim_customers')\n    description: \"Executive marketing dashboard — daily refresh, 90-day rolling window\"\n    url: https:\u002F\u002Flooker.company.com\u002Fdashboards\u002F123\n",[1550],{"type":32,"tag":52,"props":1551,"children":1552},{"__ignoreMap":16},[1553,1568,1580,1600,1617,1634,1646,1663,1680,1692,1704,1716,1733],{"type":32,"tag":95,"props":1554,"children":1555},{"class":97,"line":98},[1556,1560,1564],{"type":32,"tag":95,"props":1557,"children":1558},{"style":102},[1559],{"type":37,"value":105},{"type":32,"tag":95,"props":1561,"children":1562},{"style":108},[1563],{"type":37,"value":111},{"type":32,"tag":95,"props":1565,"children":1566},{"style":114},[1567],{"type":37,"value":117},{"type":32,"tag":95,"props":1569,"children":1570},{"class":97,"line":120},[1571,1576],{"type":32,"tag":95,"props":1572,"children":1573},{"style":102},[1574],{"type":37,"value":1575},"exposures",{"type":32,"tag":95,"props":1577,"children":1578},{"style":108},[1579],{"type":37,"value":131},{"type":32,"tag":95,"props":1581,"children":1582},{"class":97,"line":134},[1583,1587,1591,1595],{"type":32,"tag":95,"props":1584,"children":1585},{"style":108},[1586],{"type":37,"value":140},{"type":32,"tag":95,"props":1588,"children":1589},{"style":102},[1590],{"type":37,"value":145},{"type":32,"tag":95,"props":1592,"children":1593},{"style":108},[1594],{"type":37,"value":111},{"type":32,"tag":95,"props":1596,"children":1597},{"style":152},[1598],{"type":37,"value":1599},"marketing_dashboard\n",{"type":32,"tag":95,"props":1601,"children":1602},{"class":97,"line":158},[1603,1608,1612],{"type":32,"tag":95,"props":1604,"children":1605},{"style":102},[1606],{"type":37,"value":1607},"    type",{"type":32,"tag":95,"props":1609,"children":1610},{"style":108},[1611],{"type":37,"value":111},{"type":32,"tag":95,"props":1613,"children":1614},{"style":152},[1615],{"type":37,"value":1616},"dashboard\n",{"type":32,"tag":95,"props":1618,"children":1619},{"class":97,"line":176},[1620,1625,1629],{"type":32,"tag":95,"props":1621,"children":1622},{"style":102},[1623],{"type":37,"value":1624},"    maturity",{"type":32,"tag":95,"props":1626,"children":1627},{"style":108},[1628],{"type":37,"value":111},{"type":32,"tag":95,"props":1630,"children":1631},{"style":152},[1632],{"type":37,"value":1633},"high\n",{"type":32,"tag":95,"props":1635,"children":1636},{"class":97,"line":189},[1637,1642],{"type":32,"tag":95,"props":1638,"children":1639},{"style":102},[1640],{"type":37,"value":1641},"    owner",{"type":32,"tag":95,"props":1643,"children":1644},{"style":108},[1645],{"type":37,"value":131},{"type":32,"tag":95,"props":1647,"children":1648},{"class":97,"line":211},[1649,1654,1658],{"type":32,"tag":95,"props":1650,"children":1651},{"style":102},[1652],{"type":37,"value":1653},"      name",{"type":32,"tag":95,"props":1655,"children":1656},{"style":108},[1657],{"type":37,"value":111},{"type":32,"tag":95,"props":1659,"children":1660},{"style":152},[1661],{"type":37,"value":1662},"Growth Team\n",{"type":32,"tag":95,"props":1664,"children":1665},{"class":97,"line":26},[1666,1671,1675],{"type":32,"tag":95,"props":1667,"children":1668},{"style":102},[1669],{"type":37,"value":1670},"      email",{"type":32,"tag":95,"props":1672,"children":1673},{"style":108},[1674],{"type":37,"value":111},{"type":32,"tag":95,"props":1676,"children":1677},{"style":152},[1678],{"type":37,"value":1679},"growth@company.com\n",{"type":32,"tag":95,"props":1681,"children":1682},{"class":97,"line":246},[1683,1688],{"type":32,"tag":95,"props":1684,"children":1685},{"style":102},[1686],{"type":37,"value":1687},"    depends_on",{"type":32,"tag":95,"props":1689,"children":1690},{"style":108},[1691],{"type":37,"value":131},{"type":32,"tag":95,"props":1693,"children":1694},{"class":97,"line":267},[1695,1699],{"type":32,"tag":95,"props":1696,"children":1697},{"style":108},[1698],{"type":37,"value":195},{"type":32,"tag":95,"props":1700,"children":1701},{"style":152},[1702],{"type":37,"value":1703},"ref('fct_marketing_performance')\n",{"type":32,"tag":95,"props":1705,"children":1706},{"class":97,"line":284},[1707,1711],{"type":32,"tag":95,"props":1708,"children":1709},{"style":108},[1710],{"type":37,"value":195},{"type":32,"tag":95,"props":1712,"children":1713},{"style":152},[1714],{"type":37,"value":1715},"ref('dim_customers')\n",{"type":32,"tag":95,"props":1717,"children":1718},{"class":97,"line":302},[1719,1724,1728],{"type":32,"tag":95,"props":1720,"children":1721},{"style":102},[1722],{"type":37,"value":1723},"    description",{"type":32,"tag":95,"props":1725,"children":1726},{"style":108},[1727],{"type":37,"value":111},{"type":32,"tag":95,"props":1729,"children":1730},{"style":152},[1731],{"type":37,"value":1732},"\"Executive marketing dashboard — daily refresh, 90-day rolling window\"\n",{"type":32,"tag":95,"props":1734,"children":1735},{"class":97,"line":314},[1736,1741,1745],{"type":32,"tag":95,"props":1737,"children":1738},{"style":102},[1739],{"type":37,"value":1740},"    url",{"type":32,"tag":95,"props":1742,"children":1743},{"style":108},[1744],{"type":37,"value":111},{"type":32,"tag":95,"props":1746,"children":1747},{"style":152},[1748],{"type":37,"value":1749},"https:\u002F\u002Flooker.company.com\u002Fdashboards\u002F123\n",{"type":32,"tag":33,"props":1751,"children":1752},{},[1753,1755,1760,1762,1768],{"type":37,"value":1754},"Without exposure definitions, when you change ",{"type":32,"tag":52,"props":1756,"children":1758},{"className":1757},[],[1759],{"type":37,"value":1536},{"type":37,"value":1761},", you don't know which dashboard breaks. Looker shows zero metrics, you debug for 2 hours. With exposures, running ",{"type":32,"tag":52,"props":1763,"children":1765},{"className":1764},[],[1766],{"type":37,"value":1767},"dbt compile --select +exposure:marketing_dashboard",{"type":37,"value":1769}," shows all upstream models—impact analysis before you change anything.",{"type":32,"tag":33,"props":1771,"children":1772},{},[1773],{"type":37,"value":1774},"Exposures aren't just BI tools—they include reverse ETL (Hightouch, Census):",{"type":32,"tag":85,"props":1776,"children":1778},{"className":87,"code":1777,"language":89,"meta":16,"style":16},"exposures:\n  - name: meta_capi_sync\n    type: application\n    maturity: high\n    depends_on:\n      - ref('dim_customers')\n    description: \"Meta Conversion API — incremental customer events, 5-minute delay\"\n",[1779],{"type":32,"tag":52,"props":1780,"children":1781},{"__ignoreMap":16},[1782,1793,1813,1829,1844,1855,1866],{"type":32,"tag":95,"props":1783,"children":1784},{"class":97,"line":98},[1785,1789],{"type":32,"tag":95,"props":1786,"children":1787},{"style":102},[1788],{"type":37,"value":1575},{"type":32,"tag":95,"props":1790,"children":1791},{"style":108},[1792],{"type":37,"value":131},{"type":32,"tag":95,"props":1794,"children":1795},{"class":97,"line":120},[1796,1800,1804,1808],{"type":32,"tag":95,"props":1797,"children":1798},{"style":108},[1799],{"type":37,"value":140},{"type":32,"tag":95,"props":1801,"children":1802},{"style":102},[1803],{"type":37,"value":145},{"type":32,"tag":95,"props":1805,"children":1806},{"style":108},[1807],{"type":37,"value":111},{"type":32,"tag":95,"props":1809,"children":1810},{"style":152},[1811],{"type":37,"value":1812},"meta_capi_sync\n",{"type":32,"tag":95,"props":1814,"children":1815},{"class":97,"line":134},[1816,1820,1824],{"type":32,"tag":95,"props":1817,"children":1818},{"style":102},[1819],{"type":37,"value":1607},{"type":32,"tag":95,"props":1821,"children":1822},{"style":108},[1823],{"type":37,"value":111},{"type":32,"tag":95,"props":1825,"children":1826},{"style":152},[1827],{"type":37,"value":1828},"application\n",{"type":32,"tag":95,"props":1830,"children":1831},{"class":97,"line":158},[1832,1836,1840],{"type":32,"tag":95,"props":1833,"children":1834},{"style":102},[1835],{"type":37,"value":1624},{"type":32,"tag":95,"props":1837,"children":1838},{"style":108},[1839],{"type":37,"value":111},{"type":32,"tag":95,"props":1841,"children":1842},{"style":152},[1843],{"type":37,"value":1633},{"type":32,"tag":95,"props":1845,"children":1846},{"class":97,"line":176},[1847,1851],{"type":32,"tag":95,"props":1848,"children":1849},{"style":102},[1850],{"type":37,"value":1687},{"type":32,"tag":95,"props":1852,"children":1853},{"style":108},[1854],{"type":37,"value":131},{"type":32,"tag":95,"props":1856,"children":1857},{"class":97,"line":189},[1858,1862],{"type":32,"tag":95,"props":1859,"children":1860},{"style":108},[1861],{"type":37,"value":195},{"type":32,"tag":95,"props":1863,"children":1864},{"style":152},[1865],{"type":37,"value":1715},{"type":32,"tag":95,"props":1867,"children":1868},{"class":97,"line":211},[1869,1873,1877],{"type":32,"tag":95,"props":1870,"children":1871},{"style":102},[1872],{"type":37,"value":1723},{"type":32,"tag":95,"props":1874,"children":1875},{"style":108},[1876],{"type":37,"value":111},{"type":32,"tag":95,"props":1878,"children":1879},{"style":152},[1880],{"type":37,"value":1881},"\"Meta Conversion API — incremental customer events, 5-minute delay\"\n",{"type":32,"tag":33,"props":1883,"children":1884},{},[1885],{"type":37,"value":1886},"This signals: \"If you change dim_customers, you'll break the schema sent to Meta CAPI.\" In production, model update → CAPI sync error → attribution data loss is prevented by early warning.",{"type":32,"tag":40,"props":1888,"children":1890},{"id":1889},"production-pipeline-incremental-builds-and-test-coverage",[1891],{"type":37,"value":1892},"Production pipeline: Incremental builds and test coverage",{"type":32,"tag":33,"props":1894,"children":1895},{},[1896,1898,1904],{"type":37,"value":1897},"dbt in production doesn't run full refresh every day—it uses incremental models. ",{"type":32,"tag":52,"props":1899,"children":1901},{"className":1900},[],[1902],{"type":37,"value":1903},"fct_orders.sql",{"type":37,"value":1905}," reprocesses only the last 3 days:",{"type":32,"tag":85,"props":1907,"children":1909},{"className":420,"code":1908,"language":422,"meta":16,"style":16},"{{ config(\n    materialized='incremental',\n    unique_key='order_id',\n    partition_by={'field': 'order_date', 'data_type': 'date'},\n    cluster_by=['customer_id', 'channel']\n) }}\n\nSELECT\n  order_id,\n  customer_id,\n  order_date,\n  order_amount,\n  channel\nFROM {{ ref('stg_shopify_orders') }}\n\n{% if is_incremental() %}\nWHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 3 DAY)\n{% endif %}\n",[1910],{"type":32,"tag":52,"props":1911,"children":1912},{"__ignoreMap":16},[1913,1921,1942,1963,2017,2034,2041,2048,2055,2063,2071,2079,2087,2095,2115,2122,2140,2175],{"type":32,"tag":95,"props":1914,"children":1915},{"class":97,"line":98},[1916],{"type":32,"tag":95,"props":1917,"children":1918},{"style":108},[1919],{"type":37,"value":1920},"{{ config(\n",{"type":32,"tag":95,"props":1922,"children":1923},{"class":97,"line":120},[1924,1929,1933,1938],{"type":32,"tag":95,"props":1925,"children":1926},{"style":108},[1927],{"type":37,"value":1928},"    materialized",{"type":32,"tag":95,"props":1930,"children":1931},{"style":432},[1932],{"type":37,"value":839},{"type":32,"tag":95,"props":1934,"children":1935},{"style":152},[1936],{"type":37,"value":1937},"'incremental'",{"type":32,"tag":95,"props":1939,"children":1940},{"style":108},[1941],{"type":37,"value":572},{"type":32,"tag":95,"props":1943,"children":1944},{"class":97,"line":134},[1945,1950,1954,1959],{"type":32,"tag":95,"props":1946,"children":1947},{"style":108},[1948],{"type":37,"value":1949},"    unique_key",{"type":32,"tag":95,"props":1951,"children":1952},{"style":432},[1953],{"type":37,"value":839},{"type":32,"tag":95,"props":1955,"children":1956},{"style":152},[1957],{"type":37,"value":1958},"'order_id'",{"type":32,"tag":95,"props":1960,"children":1961},{"style":108},[1962],{"type":37,"value":572},{"type":32,"tag":95,"props":1964,"children":1965},{"class":97,"line":158},[1966,1971,1975,1980,1985,1989,1994,1998,2003,2007,2012],{"type":32,"tag":95,"props":1967,"children":1968},{"style":108},[1969],{"type":37,"value":1970},"    partition_by",{"type":32,"tag":95,"props":1972,"children":1973},{"style":432},[1974],{"type":37,"value":839},{"type":32,"tag":95,"props":1976,"children":1977},{"style":108},[1978],{"type":37,"value":1979},"{",{"type":32,"tag":95,"props":1981,"children":1982},{"style":152},[1983],{"type":37,"value":1984},"'field'",{"type":32,"tag":95,"props":1986,"children":1987},{"style":108},[1988],{"type":37,"value":111},{"type":32,"tag":95,"props":1990,"children":1991},{"style":152},[1992],{"type":37,"value":1993},"'order_date'",{"type":32,"tag":95,"props":1995,"children":1996},{"style":108},[1997],{"type":37,"value":622},{"type":32,"tag":95,"props":1999,"children":2000},{"style":152},[2001],{"type":37,"value":2002},"'data_type'",{"type":32,"tag":95,"props":2004,"children":2005},{"style":108},[2006],{"type":37,"value":111},{"type":32,"tag":95,"props":2008,"children":2009},{"style":152},[2010],{"type":37,"value":2011},"'date'",{"type":32,"tag":95,"props":2013,"children":2014},{"style":108},[2015],{"type":37,"value":2016},"},\n",{"type":32,"tag":95,"props":2018,"children":2019},{"class":97,"line":176},[2020,2025,2029],{"type":32,"tag":95,"props":2021,"children":2022},{"style":108},[2023],{"type":37,"value":2024},"    cluster_by",{"type":32,"tag":95,"props":2026,"children":2027},{"style":432},[2028],{"type":37,"value":839},{"type":32,"tag":95,"props":2030,"children":2031},{"style":108},[2032],{"type":37,"value":2033},"['customer_id', 'channel']\n",{"type":32,"tag":95,"props":2035,"children":2036},{"class":97,"line":189},[2037],{"type":32,"tag":95,"props":2038,"children":2039},{"style":108},[2040],{"type":37,"value":632},{"type":32,"tag":95,"props":2042,"children":2043},{"class":97,"line":211},[2044],{"type":32,"tag":95,"props":2045,"children":2046},{"emptyLinePlaceholder":1362},[2047],{"type":37,"value":1365},{"type":32,"tag":95,"props":2049,"children":2050},{"class":97,"line":26},[2051],{"type":32,"tag":95,"props":2052,"children":2053},{"style":432},[2054],{"type":37,"value":435},{"type":32,"tag":95,"props":2056,"children":2057},{"class":97,"line":246},[2058],{"type":32,"tag":95,"props":2059,"children":2060},{"style":108},[2061],{"type":37,"value":2062},"  order_id,\n",{"type":32,"tag":95,"props":2064,"children":2065},{"class":97,"line":267},[2066],{"type":32,"tag":95,"props":2067,"children":2068},{"style":108},[2069],{"type":37,"value":2070},"  customer_id,\n",{"type":32,"tag":95,"props":2072,"children":2073},{"class":97,"line":284},[2074],{"type":32,"tag":95,"props":2075,"children":2076},{"style":108},[2077],{"type":37,"value":2078},"  order_date,\n",{"type":32,"tag":95,"props":2080,"children":2081},{"class":97,"line":302},[2082],{"type":32,"tag":95,"props":2083,"children":2084},{"style":108},[2085],{"type":37,"value":2086},"  order_amount,\n",{"type":32,"tag":95,"props":2088,"children":2089},{"class":97,"line":314},[2090],{"type":32,"tag":95,"props":2091,"children":2092},{"style":108},[2093],{"type":37,"value":2094},"  channel\n",{"type":32,"tag":95,"props":2096,"children":2097},{"class":97,"line":335},[2098,2102,2106,2111],{"type":32,"tag":95,"props":2099,"children":2100},{"style":432},[2101],{"type":37,"value":607},{"type":32,"tag":95,"props":2103,"children":2104},{"style":108},[2105],{"type":37,"value":955},{"type":32,"tag":95,"props":2107,"children":2108},{"style":152},[2109],{"type":37,"value":2110},"'stg_shopify_orders'",{"type":32,"tag":95,"props":2112,"children":2113},{"style":108},[2114],{"type":37,"value":632},{"type":32,"tag":95,"props":2116,"children":2117},{"class":97,"line":1368},[2118],{"type":32,"tag":95,"props":2119,"children":2120},{"emptyLinePlaceholder":1362},[2121],{"type":37,"value":1365},{"type":32,"tag":95,"props":2123,"children":2124},{"class":97,"line":1389},[2125,2130,2135],{"type":32,"tag":95,"props":2126,"children":2127},{"style":108},[2128],{"type":37,"value":2129},"{% ",{"type":32,"tag":95,"props":2131,"children":2132},{"style":432},[2133],{"type":37,"value":2134},"if",{"type":32,"tag":95,"props":2136,"children":2137},{"style":108},[2138],{"type":37,"value":2139}," is_incremental() %}\n",{"type":32,"tag":95,"props":2141,"children":2142},{"class":97,"line":1406},[2143,2147,2152,2157,2162,2167,2171],{"type":32,"tag":95,"props":2144,"children":2145},{"style":432},[2146],{"type":37,"value":521},{"type":32,"tag":95,"props":2148,"children":2149},{"style":108},[2150],{"type":37,"value":2151}," order_date ",{"type":32,"tag":95,"props":2153,"children":2154},{"style":432},[2155],{"type":37,"value":2156},">=",{"type":32,"tag":95,"props":2158,"children":2159},{"style":108},[2160],{"type":37,"value":2161}," DATE_SUB(CURRENT_DATE(), INTERVAL ",{"type":32,"tag":95,"props":2163,"children":2164},{"style":114},[2165],{"type":37,"value":2166},"3",{"type":32,"tag":95,"props":2168,"children":2169},{"style":432},[2170],{"type":37,"value":674},{"type":32,"tag":95,"props":2172,"children":2173},{"style":108},[2174],{"type":37,"value":985},{"type":32,"tag":95,"props":2176,"children":2177},{"class":97,"line":1423},[2178],{"type":32,"tag":95,"props":2179,"children":2180},{"style":108},[2181],{"type":37,"value":2182},"{% endif %}\n",{"type":32,"tag":33,"props":2184,"children":2185},{},[2186,2188,2194],{"type":37,"value":2187},"Incremental builds cut BigQuery costs by 90%—scanning 50GB instead of 2TB. Partition + cluster boosts query performance: a ",{"type":32,"tag":52,"props":2189,"children":2191},{"className":2190},[],[2192],{"type":37,"value":2193},"WHERE customer_id = 'X'",{"type":37,"value":2195}," query hits only the relevant cluster, no full scan.",{"type":32,"tag":33,"props":2197,"children":2198},{},[2199,2201,2207],{"type":37,"value":2200},"Test coverage is critical. In dbt's ",{"type":32,"tag":52,"props":2202,"children":2204},{"className":2203},[],[2205],{"type":37,"value":2206},"schema.yml",{"type":37,"value":2208},", you write tests for every model:",{"type":32,"tag":85,"props":2210,"children":2212},{"className":87,"code":2211,"language":89,"meta":16,"style":16},"models:\n  - name: fct_orders\n    columns:\n      - name: order_id\n        tests:\n          - unique\n          - not_null\n      - name: order_amount\n        tests:\n          - not_null\n          - dbt_utils.expression_is_true:\n              expression: \">= 0\"\n      - name: order_date\n        tests:\n          - dbt_utils.recency:\n              datepart: day\n              interval: 7\n",[2213],{"type":32,"tag":52,"props":2214,"children":2215},{"__ignoreMap":16},[2216,2228,2248,2260,2280,2292,2305,2317,2336,2347,2358,2374,2391,2410,2421,2437,2454],{"type":32,"tag":95,"props":2217,"children":2218},{"class":97,"line":98},[2219,2224],{"type":32,"tag":95,"props":2220,"children":2221},{"style":102},[2222],{"type":37,"value":2223},"models",{"type":32,"tag":95,"props":2225,"children":2226},{"style":108},[2227],{"type":37,"value":131},{"type":32,"tag":95,"props":2229,"children":2230},{"class":97,"line":120},[2231,2235,2239,2243],{"type":32,"tag":95,"props":2232,"children":2233},{"style":108},[2234],{"type":37,"value":140},{"type":32,"tag":95,"props":2236,"children":2237},{"style":102},[2238],{"type":37,"value":145},{"type":32,"tag":95,"props":2240,"children":2241},{"style":108},[2242],{"type":37,"value":111},{"type":32,"tag":95,"props":2244,"children":2245},{"style":152},[2246],{"type":37,"value":2247},"fct_orders\n",{"type":32,"tag":95,"props":2249,"children":2250},{"class":97,"line":134},[2251,2256],{"type":32,"tag":95,"props":2252,"children":2253},{"style":102},[2254],{"type":37,"value":2255},"    columns",{"type":32,"tag":95,"props":2257,"children":2258},{"style":108},[2259],{"type":37,"value":131},{"type":32,"tag":95,"props":2261,"children":2262},{"class":97,"line":158},[2263,2267,2271,2275],{"type":32,"tag":95,"props":2264,"children":2265},{"style":108},[2266],{"type":37,"value":195},{"type":32,"tag":95,"props":2268,"children":2269},{"style":102},[2270],{"type":37,"value":145},{"type":32,"tag":95,"props":2272,"children":2273},{"style":108},[2274],{"type":37,"value":111},{"type":32,"tag":95,"props":2276,"children":2277},{"style":152},[2278],{"type":37,"value":2279},"order_id\n",{"type":32,"tag":95,"props":2281,"children":2282},{"class":97,"line":176},[2283,2288],{"type":32,"tag":95,"props":2284,"children":2285},{"style":102},[2286],{"type":37,"value":2287},"        tests",{"type":32,"tag":95,"props":2289,"children":2290},{"style":108},[2291],{"type":37,"value":131},{"type":32,"tag":95,"props":2293,"children":2294},{"class":97,"line":189},[2295,2300],{"type":32,"tag":95,"props":2296,"children":2297},{"style":108},[2298],{"type":37,"value":2299},"          - ",{"type":32,"tag":95,"props":2301,"children":2302},{"style":152},[2303],{"type":37,"value":2304},"unique\n",{"type":32,"tag":95,"props":2306,"children":2307},{"class":97,"line":211},[2308,2312],{"type":32,"tag":95,"props":2309,"children":2310},{"style":108},[2311],{"type":37,"value":2299},{"type":32,"tag":95,"props":2313,"children":2314},{"style":152},[2315],{"type":37,"value":2316},"not_null\n",{"type":32,"tag":95,"props":2318,"children":2319},{"class":97,"line":26},[2320,2324,2328,2332],{"type":32,"tag":95,"props":2321,"children":2322},{"style":108},[2323],{"type":37,"value":195},{"type":32,"tag":95,"props":2325,"children":2326},{"style":102},[2327],{"type":37,"value":145},{"type":32,"tag":95,"props":2329,"children":2330},{"style":108},[2331],{"type":37,"value":111},{"type":32,"tag":95,"props":2333,"children":2334},{"style":152},[2335],{"type":37,"value":1241},{"type":32,"tag":95,"props":2337,"children":2338},{"class":97,"line":246},[2339,2343],{"type":32,"tag":95,"props":2340,"children":2341},{"style":102},[2342],{"type":37,"value":2287},{"type":32,"tag":95,"props":2344,"children":2345},{"style":108},[2346],{"type":37,"value":131},{"type":32,"tag":95,"props":2348,"children":2349},{"class":97,"line":267},[2350,2354],{"type":32,"tag":95,"props":2351,"children":2352},{"style":108},[2353],{"type":37,"value":2299},{"type":32,"tag":95,"props":2355,"children":2356},{"style":152},[2357],{"type":37,"value":2316},{"type":32,"tag":95,"props":2359,"children":2360},{"class":97,"line":284},[2361,2365,2370],{"type":32,"tag":95,"props":2362,"children":2363},{"style":108},[2364],{"type":37,"value":2299},{"type":32,"tag":95,"props":2366,"children":2367},{"style":102},[2368],{"type":37,"value":2369},"dbt_utils.expression_is_true",{"type":32,"tag":95,"props":2371,"children":2372},{"style":108},[2373],{"type":37,"value":131},{"type":32,"tag":95,"props":2375,"children":2376},{"class":97,"line":302},[2377,2382,2386],{"type":32,"tag":95,"props":2378,"children":2379},{"style":102},[2380],{"type":37,"value":2381},"              expression",{"type":32,"tag":95,"props":2383,"children":2384},{"style":108},[2385],{"type":37,"value":111},{"type":32,"tag":95,"props":2387,"children":2388},{"style":152},[2389],{"type":37,"value":2390},"\">= 0\"\n",{"type":32,"tag":95,"props":2392,"children":2393},{"class":97,"line":314},[2394,2398,2402,2406],{"type":32,"tag":95,"props":2395,"children":2396},{"style":108},[2397],{"type":37,"value":195},{"type":32,"tag":95,"props":2399,"children":2400},{"style":102},[2401],{"type":37,"value":145},{"type":32,"tag":95,"props":2403,"children":2404},{"style":108},[2405],{"type":37,"value":111},{"type":32,"tag":95,"props":2407,"children":2408},{"style":152},[2409],{"type":37,"value":1258},{"type":32,"tag":95,"props":2411,"children":2412},{"class":97,"line":335},[2413,2417],{"type":32,"tag":95,"props":2414,"children":2415},{"style":102},[2416],{"type":37,"value":2287},{"type":32,"tag":95,"props":2418,"children":2419},{"style":108},[2420],{"type":37,"value":131},{"type":32,"tag":95,"props":2422,"children":2423},{"class":97,"line":1368},[2424,2428,2433],{"type":32,"tag":95,"props":2425,"children":2426},{"style":108},[2427],{"type":37,"value":2299},{"type":32,"tag":95,"props":2429,"children":2430},{"style":102},[2431],{"type":37,"value":2432},"dbt_utils.recency",{"type":32,"tag":95,"props":2434,"children":2435},{"style":108},[2436],{"type":37,"value":131},{"type":32,"tag":95,"props":2438,"children":2439},{"class":97,"line":1389},[2440,2445,2449],{"type":32,"tag":95,"props":2441,"children":2442},{"style":102},[2443],{"type":37,"value":2444},"              datepart",{"type":32,"tag":95,"props":2446,"children":2447},{"style":108},[2448],{"type":37,"value":111},{"type":32,"tag":95,"props":2450,"children":2451},{"style":152},[2452],{"type":37,"value":2453},"day\n",{"type":32,"tag":95,"props":2455,"children":2456},{"class":97,"line":1406},[2457,2462,2466],{"type":32,"tag":95,"props":2458,"children":2459},{"style":102},[2460],{"type":37,"value":2461},"              interval",{"type":32,"tag":95,"props":2463,"children":2464},{"style":108},[2465],{"type":37,"value":111},{"type":32,"tag":95,"props":2467,"children":2468},{"style":114},[2469],{"type":37,"value":2470},"7\n",{"type":32,"tag":33,"props":2472,"children":2473},{},[2474,2480,2482,2488],{"type":32,"tag":52,"props":2475,"children":2477},{"className":2476},[],[2478],{"type":37,"value":2479},"dbt test",{"type":37,"value":2481}," asserts these conditions in BigQuery—if order_amount goes negative, the build fails. In production, every commit runs through CI\u002FCD: ",{"type":32,"tag":52,"props":2483,"children":2485},{"className":2484},[],[2486],{"type":37,"value":2487},"dbt run --select state:modified+ → dbt test --select state:modified+",{"type":37,"value":2489},". Modified models and downstream dependencies run and test; if all passes, merge is allowed.",{"type":32,"tag":40,"props":2491,"children":2493},{"id":2492},"orchestration-airflow-prefect-dbt-cloud",[2494],{"type":37,"value":2495},"Orchestration: Airflow, Prefect, dbt Cloud",{"type":32,"tag":33,"props":2497,"children":2498},{},[2499],{"type":37,"value":2500},"dbt itself isn't an orchestrator—Airflow or Prefect schedules it. Example Airflow DAG:",{"type":32,"tag":85,"props":2502,"children":2506},{"className":2503,"code":2504,"language":2505,"meta":16,"style":16},"language-python shiki shiki-themes github-dark","from airflow.providers.google.cloud.operators.bigquery import BigQueryInsertJobOperator\nfrom airflow.operators.bash import BashOperator\n\ndbt_run = BashOperator(\n    task_id='dbt_run',\n    bash_command='cd \u002Fopt\u002Fdbt && dbt run --profiles-dir .',\n    dag=dag\n)\n\ndbt_test = BashOperator(\n    task_id='dbt_test',\n    bash_command='cd \u002Fopt\u002Fdbt && dbt test',\n    dag=dag\n)\n\ndbt_run >> dbt_test\n","python",[2507],{"type":32,"tag":52,"props":2508,"children":2509},{"__ignoreMap":16},[2510,2533,2554,2561,2578,2600,2621,2638,2645,2652,2668,2688,2708,2723,2730,2737],{"type":32,"tag":95,"props":2511,"children":2512},{"class":97,"line":98},[2513,2518,2523,2528],{"type":32,"tag":95,"props":2514,"children":2515},{"style":432},[2516],{"type":37,"value":2517},"from",{"type":32,"tag":95,"props":2519,"children":2520},{"style":108},[2521],{"type":37,"value":2522}," airflow.providers.google.cloud.operators.bigquery ",{"type":32,"tag":95,"props":2524,"children":2525},{"style":432},[2526],{"type":37,"value":2527},"import",{"type":32,"tag":95,"props":2529,"children":2530},{"style":108},[2531],{"type":37,"value":2532}," BigQueryInsertJobOperator\n",{"type":32,"tag":95,"props":2534,"children":2535},{"class":97,"line":120},[2536,2540,2545,2549],{"type":32,"tag":95,"props":2537,"children":2538},{"style":432},[2539],{"type":37,"value":2517},{"type":32,"tag":95,"props":2541,"children":2542},{"style":108},[2543],{"type":37,"value":2544}," airflow.operators.bash ",{"type":32,"tag":95,"props":2546,"children":2547},{"style":432},[2548],{"type":37,"value":2527},{"type":32,"tag":95,"props":2550,"children":2551},{"style":108},[2552],{"type":37,"value":2553}," BashOperator\n",{"type":32,"tag":95,"props":2555,"children":2556},{"class":97,"line":134},[2557],{"type":32,"tag":95,"props":2558,"children":2559},{"emptyLinePlaceholder":1362},[2560],{"type":37,"value":1365},{"type":32,"tag":95,"props":2562,"children":2563},{"class":97,"line":158},[2564,2569,2573],{"type":32,"tag":95,"props":2565,"children":2566},{"style":108},[2567],{"type":37,"value":2568},"dbt_run ",{"type":32,"tag":95,"props":2570,"children":2571},{"style":432},[2572],{"type":37,"value":839},{"type":32,"tag":95,"props":2574,"children":2575},{"style":108},[2576],{"type":37,"value":2577}," BashOperator(\n",{"type":32,"tag":95,"props":2579,"children":2580},{"class":97,"line":176},[2581,2587,2591,2596],{"type":32,"tag":95,"props":2582,"children":2584},{"style":2583},"--shiki-default:#FFAB70",[2585],{"type":37,"value":2586},"    task_id",{"type":32,"tag":95,"props":2588,"children":2589},{"style":432},[2590],{"type":37,"value":839},{"type":32,"tag":95,"props":2592,"children":2593},{"style":152},[2594],{"type":37,"value":2595},"'dbt_run'",{"type":32,"tag":95,"props":2597,"children":2598},{"style":108},[2599],{"type":37,"value":572},{"type":32,"tag":95,"props":2601,"children":2602},{"class":97,"line":189},[2603,2608,2612,2617],{"type":32,"tag":95,"props":2604,"children":2605},{"style":2583},[2606],{"type":37,"value":2607},"    bash_command",{"type":32,"tag":95,"props":2609,"children":2610},{"style":432},[2611],{"type":37,"value":839},{"type":32,"tag":95,"props":2613,"children":2614},{"style":152},[2615],{"type":37,"value":2616},"'cd \u002Fopt\u002Fdbt && dbt run --profiles-dir .'",{"type":32,"tag":95,"props":2618,"children":2619},{"style":108},[2620],{"type":37,"value":572},{"type":32,"tag":95,"props":2622,"children":2623},{"class":97,"line":211},[2624,2629,2633],{"type":32,"tag":95,"props":2625,"children":2626},{"style":2583},[2627],{"type":37,"value":2628},"    dag",{"type":32,"tag":95,"props":2630,"children":2631},{"style":432},[2632],{"type":37,"value":839},{"type":32,"tag":95,"props":2634,"children":2635},{"style":108},[2636],{"type":37,"value":2637},"dag\n",{"type":32,"tag":95,"props":2639,"children":2640},{"class":97,"line":26},[2641],{"type":32,"tag":95,"props":2642,"children":2643},{"style":108},[2644],{"type":37,"value":985},{"type":32,"tag":95,"props":2646,"children":2647},{"class":97,"line":246},[2648],{"type":32,"tag":95,"props":2649,"children":2650},{"emptyLinePlaceholder":1362},[2651],{"type":37,"value":1365},{"type":32,"tag":95,"props":2653,"children":2654},{"class":97,"line":267},[2655,2660,2664],{"type":32,"tag":95,"props":2656,"children":2657},{"style":108},[2658],{"type":37,"value":2659},"dbt_test ",{"type":32,"tag":95,"props":2661,"children":2662},{"style":432},[2663],{"type":37,"value":839},{"type":32,"tag":95,"props":2665,"children":2666},{"style":108},[2667],{"type":37,"value":2577},{"type":32,"tag":95,"props":2669,"children":2670},{"class":97,"line":284},[2671,2675,2679,2684],{"type":32,"tag":95,"props":2672,"children":2673},{"style":2583},[2674],{"type":37,"value":2586},{"type":32,"tag":95,"props":2676,"children":2677},{"style":432},[2678],{"type":37,"value":839},{"type":32,"tag":95,"props":2680,"children":2681},{"style":152},[2682],{"type":37,"value":2683},"'dbt_test'",{"type":32,"tag":95,"props":2685,"children":2686},{"style":108},[2687],{"type":37,"value":572},{"type":32,"tag":95,"props":2689,"children":2690},{"class":97,"line":302},[2691,2695,2699,2704],{"type":32,"tag":95,"props":2692,"children":2693},{"style":2583},[2694],{"type":37,"value":2607},{"type":32,"tag":95,"props":2696,"children":2697},{"style":432},[2698],{"type":37,"value":839},{"type":32,"tag":95,"props":2700,"children":2701},{"style":152},[2702],{"type":37,"value":2703},"'cd \u002Fopt\u002Fdbt && dbt test'",{"type":32,"tag":95,"props":2705,"children":2706},{"style":108},[2707],{"type":37,"value":572},{"type":32,"tag":95,"props":2709,"children":2710},{"class":97,"line":314},[2711,2715,2719],{"type":32,"tag":95,"props":2712,"children":2713},{"style":2583},[2714],{"type":37,"value":2628},{"type":32,"tag":95,"props":2716,"children":2717},{"style":432},[2718],{"type":37,"value":839},{"type":32,"tag":95,"props":2720,"children":2721},{"style":108},[2722],{"type":37,"value":2637},{"type":32,"tag":95,"props":2724,"children":2725},{"class":97,"line":335},[2726],{"type":32,"tag":95,"props":2727,"children":2728},{"style":108},[2729],{"type":37,"value":985},{"type":32,"tag":95,"props":2731,"children":2732},{"class":97,"line":1368},[2733],{"type":32,"tag":95,"props":2734,"children":2735},{"emptyLinePlaceholder":1362},[2736],{"type":37,"value":1365},{"type":32,"tag":95,"props":2738,"children":2739},{"class":97,"line":1389},[2740,2744,2749],{"type":32,"tag":95,"props":2741,"children":2742},{"style":108},[2743],{"type":37,"value":2568},{"type":32,"tag":95,"props":2745,"children":2746},{"style":432},[2747],{"type":37,"value":2748},">>",{"type":32,"tag":95,"props":2750,"children":2751},{"style":108},[2752],{"type":37,"value":2753}," dbt_test\n",{"type":32,"tag":33,"props":2755,"children":2756},{},[2757],{"type":37,"value":2758},"dbt Cloud is the alternative—managed orchestration, Web IDE, Slack alerts. Most enterprises prefer Airflow because there are tasks beyond dbt: upstream API pulls, downstream reverse ETL, snapshot tables.",{"type":32,"tag":33,"props":2760,"children":2761},{},[2762,2764,2770,2772,2778],{"type":37,"value":2763},"Schedule strategy ties to data freshness. GA4 events have 24-hour processing lag (processing_date ≠ event_date); Meta Ads Insights API isn't real-time. Staging models trigger on source freshness—when GA4 sends a new partition, ",{"type":32,"tag":52,"props":2765,"children":2767},{"className":2766},[],[2768],{"type":37,"value":2769},"stg_ga4_events",{"type":37,"value":2771}," refreshes, cascading through intermediate → mart. An Airflow sensor checks BigQuery's ",{"type":32,"tag":52,"props":2773,"children":2775},{"className":2774},[],[2776],{"type":37,"value":2777},"_TABLE_SUFFIX",{"type":37,"value":83},{"type":32,"tag":85,"props":2780,"children":2782},{"className":2503,"code":2781,"language":2505,"meta":16,"style":16},"wait_for_ga4 = BigQueryTableExistenceSensor(\n    task_id='wait_for_ga4_partition',\n    project_id='analytics_123456',\n    dataset_id='events_',\n    table_id=f\"events_{yesterday.strftime('%Y%m%d')}\",\n    poke_interval=300\n)\n",[2783],{"type":32,"tag":52,"props":2784,"children":2785},{"__ignoreMap":16},[2786,2803,2823,2844,2864,2929,2946],{"type":32,"tag":95,"props":2787,"children":2788},{"class":97,"line":98},[2789,2794,2798],{"type":32,"tag":95,"props":2790,"children":2791},{"style":108},[2792],{"type":37,"value":2793},"wait_for_ga4 ",{"type":32,"tag":95,"props":2795,"children":2796},{"style":432},[2797],{"type":37,"value":839},{"type":32,"tag":95,"props":2799,"children":2800},{"style":108},[2801],{"type":37,"value":2802}," BigQueryTableExistenceSensor(\n",{"type":32,"tag":95,"props":2804,"children":2805},{"class":97,"line":120},[2806,2810,2814,2819],{"type":32,"tag":95,"props":2807,"children":2808},{"style":2583},[2809],{"type":37,"value":2586},{"type":32,"tag":95,"props":2811,"children":2812},{"style":432},[2813],{"type":37,"value":839},{"type":32,"tag":95,"props":2815,"children":2816},{"style":152},[2817],{"type":37,"value":2818},"'wait_for_ga4_partition'",{"type":32,"tag":95,"props":2820,"children":2821},{"style":108},[2822],{"type":37,"value":572},{"type":32,"tag":95,"props":2824,"children":2825},{"class":97,"line":134},[2826,2831,2835,2840],{"type":32,"tag":95,"props":2827,"children":2828},{"style":2583},[2829],{"type":37,"value":2830},"    project_id",{"type":32,"tag":95,"props":2832,"children":2833},{"style":432},[2834],{"type":37,"value":839},{"type":32,"tag":95,"props":2836,"children":2837},{"style":152},[2838],{"type":37,"value":2839},"'analytics_123456'",{"type":32,"tag":95,"props":2841,"children":2842},{"style":108},[2843],{"type":37,"value":572},{"type":32,"tag":95,"props":2845,"children":2846},{"class":97,"line":158},[2847,2852,2856,2860],{"type":32,"tag":95,"props":2848,"children":2849},{"style":2583},[2850],{"type":37,"value":2851},"    dataset_id",{"type":32,"tag":95,"props":2853,"children":2854},{"style":432},[2855],{"type":37,"value":839},{"type":32,"tag":95,"props":2857,"children":2858},{"style":152},[2859],{"type":37,"value":627},{"type":32,"tag":95,"props":2861,"children":2862},{"style":108},[2863],{"type":37,"value":572},{"type":32,"tag":95,"props":2865,"children":2866},{"class":97,"line":176},[2867,2872,2876,2881,2886,2890,2895,2900,2905,2910,2915,2920,2925],{"type":32,"tag":95,"props":2868,"children":2869},{"style":2583},[2870],{"type":37,"value":2871},"    table_id",{"type":32,"tag":95,"props":2873,"children":2874},{"style":432},[2875],{"type":37,"value":839},{"type":32,"tag":95,"props":2877,"children":2878},{"style":432},[2879],{"type":37,"value":2880},"f",{"type":32,"tag":95,"props":2882,"children":2883},{"style":152},[2884],{"type":37,"value":2885},"\"events_",{"type":32,"tag":95,"props":2887,"children":2888},{"style":114},[2889],{"type":37,"value":1979},{"type":32,"tag":95,"props":2891,"children":2892},{"style":108},[2893],{"type":37,"value":2894},"yesterday.strftime(",{"type":32,"tag":95,"props":2896,"children":2897},{"style":152},[2898],{"type":37,"value":2899},"'%Y%m",{"type":32,"tag":95,"props":2901,"children":2902},{"style":114},[2903],{"type":37,"value":2904},"%d",{"type":32,"tag":95,"props":2906,"children":2907},{"style":152},[2908],{"type":37,"value":2909},"'",{"type":32,"tag":95,"props":2911,"children":2912},{"style":108},[2913],{"type":37,"value":2914},")",{"type":32,"tag":95,"props":2916,"children":2917},{"style":114},[2918],{"type":37,"value":2919},"}",{"type":32,"tag":95,"props":2921,"children":2922},{"style":152},[2923],{"type":37,"value":2924},"\"",{"type":32,"tag":95,"props":2926,"children":2927},{"style":108},[2928],{"type":37,"value":572},{"type":32,"tag":95,"props":2930,"children":2931},{"class":97,"line":189},[2932,2937,2941],{"type":32,"tag":95,"props":2933,"children":2934},{"style":2583},[2935],{"type":37,"value":2936},"    poke_interval",{"type":32,"tag":95,"props":2938,"children":2939},{"style":432},[2940],{"type":37,"value":839},{"type":32,"tag":95,"props":2942,"children":2943},{"style":114},[2944],{"type":37,"value":2945},"300\n",{"type":32,"tag":95,"props":2947,"children":2948},{"class":97,"line":211},[2949],{"type":32,"tag":95,"props":2950,"children":2951},{"style":108},[2952],{"type":37,"value":985},{"type":32,"tag":33,"props":2954,"children":2955},{},[2956],{"type":37,"value":2957},"Once the partition is ready, the dbt chain triggers. This pattern solves late-arriving data—instead of blocking on API delays, the pipeline waits.",{"type":32,"tag":40,"props":2959,"children":2961},{"id":2960},"tradeoffs-what-dbt-doesnt-solve",[2962],{"type":37,"value":2963},"Tradeoffs: What dbt doesn't solve",{"type":32,"tag":33,"props":2965,"children":2966},{},[2967],{"type":37,"value":2968},"dbt is a transformation engine, not a data loader. Who pulls data into BigQuery? Fivetran, Airbyte, custom Python scripts. dbt assumes raw data is already there in its source definitions. The pattern is ELT: Extract-Load-Transform. Different from ETL—the Transform happens inside the warehouse. dbt handles the T; EL is a separate toolchain.",{"type":32,"tag":33,"props":2970,"children":2971},{},[2972],{"type":37,"value":2973},"dbt doesn't support real-time streaming. Kafka → BigQuery streaming insert → dbt incremental model chain introduces minute-level latency. Sub-second use cases (fraud detection, dynamic pricing) need stream processors—Flink, Spark Structured Streaming, or Materialize. dbt isn't the tool.",{"type":32,"tag":33,"props":2975,"children":2976},{},[2977],{"type":37,"value":2978},"dbt's Python model support (v1.3+) is limited. You can do pandas dataframe manipulation:",{"type":32,"tag":85,"props":2980,"children":2982},{"className":2503,"code":2981,"language":2505,"meta":16,"style":16},"def model(dbt, session):\n    df = dbt.ref('stg_orders').to_pandas()\n    df['log_amount'] = np.log1p(df['order_amount'])\n    return df\n",[2983],{"type":32,"tag":52,"props":2984,"children":2985},{"__ignoreMap":16},[2986,3005,3032,3069],{"type":32,"tag":95,"props":2987,"children":2988},{"class":97,"line":98},[2989,2994,3000],{"type":32,"tag":95,"props":2990,"children":2991},{"style":432},[2992],{"type":37,"value":2993},"def",{"type":32,"tag":95,"props":2995,"children":2997},{"style":2996},"--shiki-default:#B392F0",[2998],{"type":37,"value":2999}," model",{"type":32,"tag":95,"props":3001,"children":3002},{"style":108},[3003],{"type":37,"value":3004},"(dbt, session):\n",{"type":32,"tag":95,"props":3006,"children":3007},{"class":97,"line":120},[3008,3013,3017,3022,3027],{"type":32,"tag":95,"props":3009,"children":3010},{"style":108},[3011],{"type":37,"value":3012},"    df ",{"type":32,"tag":95,"props":3014,"children":3015},{"style":432},[3016],{"type":37,"value":839},{"type":32,"tag":95,"props":3018,"children":3019},{"style":108},[3020],{"type":37,"value":3021}," dbt.ref(",{"type":32,"tag":95,"props":3023,"children":3024},{"style":152},[3025],{"type":37,"value":3026},"'stg_orders'",{"type":32,"tag":95,"props":3028,"children":3029},{"style":108},[3030],{"type":37,"value":3031},").to_pandas()\n",{"type":32,"tag":95,"props":3033,"children":3034},{"class":97,"line":134},[3035,3040,3045,3050,3054,3059,3064],{"type":32,"tag":95,"props":3036,"children":3037},{"style":108},[3038],{"type":37,"value":3039},"    df[",{"type":32,"tag":95,"props":3041,"children":3042},{"style":152},[3043],{"type":37,"value":3044},"'log_amount'",{"type":32,"tag":95,"props":3046,"children":3047},{"style":108},[3048],{"type":37,"value":3049},"] ",{"type":32,"tag":95,"props":3051,"children":3052},{"style":432},[3053],{"type":37,"value":839},{"type":32,"tag":95,"props":3055,"children":3056},{"style":108},[3057],{"type":37,"value":3058}," np.log1p(df[",{"type":32,"tag":95,"props":3060,"children":3061},{"style":152},[3062],{"type":37,"value":3063},"'order_amount'",{"type":32,"tag":95,"props":3065,"children":3066},{"style":108},[3067],{"type":37,"value":3068},"])\n",{"type":32,"tag":95,"props":3070,"children":3071},{"class":97,"line":158},[3072,3077],{"type":32,"tag":95,"props":3073,"children":3074},{"style":432},[3075],{"type":37,"value":3076},"    return",{"type":32,"tag":95,"props":3078,"children":3079},{"style":108},[3080],{"type":37,"value":3081}," df\n",{"type":32,"tag":33,"props":3083,"children":3084},{},[3085],{"type":37,"value":3086},"But you're not training scikit-learn models here. BigQuery compute is expensive, Python runtime overhead is high. Complex transformations run faster in SQL. Feature engineering lives in dbt; model training happens in Vertex AI; inference in BigQuery ML. That's the pattern.",{"type":32,"tag":40,"props":3088,"children":3090},{"id":3089},"what-to-do-now",[3091],{"type":37,"value":3092},"What to do now",{"type":32,"tag":33,"props":3094,"children":3095},{},[3096,3098,3103,3105,3110],{"type":37,"value":3097},"If your marketing data still lives in manually stitched spreadsheets, the first move is setting up raw data flow to BigQuery. GA4 export, Meta\u002FGoogle Ads API connectors (Fivetran, Supermetrics), CRM webhooks → BigQuery streaming insert. Once raw data is there, you create a dbt repository: staging models for source mapping, intermediate models for sessionization and attribution, mart models for final KPIs. In the first two weeks, ",{"type":32,"tag":52,"props":3099,"children":3101},{"className":3100},[],[3102],{"type":37,"value":1075},{"type":37,"value":3104}," and ",{"type":32,"tag":52,"props":3106,"children":3108},{"className":3107},[],[3109],{"type":37,"value":1068},{"type":37,"value":3111}," are enough—dashboards point here, metrics stabilize. The semantic layer lands in week three, exposure mapping in week four. By week six, your production pipeline is git-controlled, test-covered, and incremental-optimized. Spreadsheets become read-only archives.",{"type":32,"tag":3113,"props":3114,"children":3115},"style",{},[3116],{"type":37,"value":3117},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":16,"searchDepth":134,"depth":134,"links":3119},[3120,3121,3122,3123,3124,3125,3126,3127],{"id":42,"depth":120,"text":45},{"id":402,"depth":120,"text":405},{"id":1104,"depth":120,"text":1107},{"id":1522,"depth":120,"text":1525},{"id":1889,"depth":120,"text":1892},{"id":2492,"depth":120,"text":2495},{"id":2960,"depth":120,"text":2963},{"id":3089,"depth":120,"text":3092},"markdown","content:en:data:dbt-bigquery-modern-marketing-data-stack.md","content","en\u002Fdata\u002Fdbt-bigquery-modern-marketing-data-stack.md","en\u002Fdata\u002Fdbt-bigquery-modern-marketing-data-stack","md",1782050750590]