Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - ananis25/funsql-dbt: example project to demonstrate working with the FunSQL library · GitHub
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - ananis25/funsql-dbt: example project to demonstrate working with the FunSQL library · GitHub
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - ananis25/funsql-dbt: example project to demonstrate working with the FunSQL library · GitHub
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - ananis25/funsql-dbt: example project to demonstrate working with the FunSQL library · GitHub
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - ananis25/funsql-dbt: example project to demonstrate working with the FunSQL library · GitHub
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - ananis25/funsql-dbt: example project to demonstrate working with the FunSQL library · GitHub
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - ananis25/funsql-dbt: example project to demonstrate working with the FunSQL library · GitHub
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

funsql-dbt

This folder hosts a DBT like script to go over a dependency tree of query views/tables, and materialize them to a data warehouse. The FunSQL library helps compose SQL queries, we could do away with a templating language like Jinja.

It isn't particularly desirable! DBT fits the interactive workflow - models are SQL queries introduced one at a time, with some Jinja markup/macros. That is a more declarative approach than combining python functions.

However, writing transform steps using a python DSL affords some flexibility.

  • Construct queries dynamically - you can use regular python control flow, choose to only materialize tables that have say, multiple children, share logic/parameters more easily, etc.
  • Using alternate orchestration engines like Prefect/Dagster - you can still get workflow semantics for the whole process, like error recovery and retries, but get more control over the execution.

I tried to reproduce the jaffle shopexample from the DBT tutorial. We use a sqlite database file, and no other dependencies. Though it should be easy to use a workflow tool like Prefect to make table materializations as discrete tasks, and get caching/scheduling and other good stuff.

Using functions

Change to the funsql_dbt directory and run function_models.py.

Each data model is a function, that returns a FunSQL query. We decorate the functions to specify if the output of that query should be materialized, and the table name for it. The task runner takes as input a list of data models to materialize, then descends down the dependency tree and also executes any intermediate models.

The resulting code is short enough, but setting up model dependencies is clunky. We could pass them as arguments to each model function, but wiring models together everytime is tedious. So, instead we call the parent models directly inside the model code, but now we lose any visibility of the dependency graph. That also means execution can only be sequential.

Using classes

Change to the funsql_dbt directory and run class_models.py.

Each data model is a class object, with the other data models it depends on as attributes. Now, we can get the dependency graph by inspecting the code, and optimize how to go about generating the tables/views.

  • The task runner get a list of models to materialize. It descends down the dependency tree and creates a topological order over all the models.

  • Now, it can execute the models starting from the ones without any parent models. Wiring up models is also straightforward since inspecting the class definition tells us the dependencies, and topological sort ensures they have been executed first. With data warehouses that are happy to run concurrent queries, we can also execute models in parallel that are not blocked on any parent models finishing first.

  • To share parameters across models, we create a single context store for all models, and pass it along for all executions.

About

example project to demonstrate working with the FunSQL library

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages