En l’arquitectura d’analítica de dades que implementem des de Coopdevs, els processos d’ETL (extracció, transformació i càrrega de dades) els fem mitjançant DBT. DBT et permet generar automàticament documentació web agafant, per una banda, la documentació generada en els fitxers yml de definició de les fonts i els models, i per altra banda, el datalineage entre les diferents taules.
A través de la comanda:
dbt docs generate
DBT genera una pàgina web on es pot veure una fitxa amb el detall de cada taula amb els seus camps (generat automàticament per dbt), la descripció (agafat dels fitxers yml) i el data lineage, tant dins de la fitxa com en format gràfic.
A partir de les taules generades amb DBT creem datasets, gràfics i quadres de comandament a Superset. Si volem tenir un datalineage complert del nostre projecte hem d’integrar aquesta part també a la documentació, i, a poder ser, que sigui el més automàtic possible.
Integració catàleg de Superset a DBT
DBT permet integrar documentació externa a través d’un fitxer que es diu exposures.yml
Per a que DBT detecti aquest fitxer, ha d’estar a la carpeta on tenim els models.
L’estructura del fitxer és:
exposures: - name: <nom> (ex. DATASET - nom del dataset) label: nom del dataset type: <notebook, analysis, dashboard> url: url del dataset, chart o dashboard de Superset description: Descripció de l'element depends_on: llista de dependències de DBT owner: name: owner name email: owner email
Com podem veure, a type només podem tenir els valors: notebook, analysis i dashboard. Per a integrar les dades amb el catàleg de Superset hem decidit les següents relacions:
- Els quadres de comandament de Superset els classificarem com a dashboard.
- Els gràfics de Superset els classificarem com a analysis.
- Els datasets de Superset els classificarem com a notebook.
A partir d’aquesta classificació llegirem del catàleg de Superset per anar omplint la resta de camps.
Generació de dades dels datasets
Amb la consulta:
select t.id, t.table_name, coalesce(description,'Sense descripció') as description, d.database_name, t.sql from tables t join dbs d on t.database_id= d.id order by table_name
sobre la base de dades de Superset podem extreure les dades dels datasets.
La url de Superset per accedir-hi és <url mare Superset>/explore/?datasource_type=table&datasource_id=<id>
A través de la funció de Python sql_metadata.Parser podrem parsejar la consulta del dataset (si és un dataset virtual) per a extreure les dependències de DBT.
En el subapartat depends on, haurem de posar les referències a les taules que ens ha retornat el pas anterior en el format DBT (ex. ref(‘nom de la taula’) o source(‘esquema’, ‘nom de la taula’), depenent si són models o fonts de dades).
Amb la consulta:
select s.id, slice_name, coalesce(s.description,'Sense descripció') as description, viz_type , t.table_name as datasetname, t.id as datasetid, coalesce(s.certified_by, '') as certified_by from slices s join tables t on s.datasource_id = t.id order by slice_name
Podrem extreure quins gràfics fan servir aquest dataset i posar-ho a la descripció, creant enllaços interns per accedir a la descripció del chart a la pròpia documentació.
Generació de dades dels gràfics.
Per a la generació de dades per a la documentació dels charts seguirem una estratègia similar que amb els datasets.
Amb la consulta:
select s.id, slice_name, coalesce(s.description,'Sense descripció') as description, viz_type , t.table_name as datasetname, t.id as datasetid, coalesce(s.certified_by, '') as certified_by from slices s join tables t on s.datasource_id = t.id order by slice_name
Podrem extreure les dades de cada chart, incloent el dataset que es fa servir.
La url de Superset per accedir-hi és <url mare Superset>/explore/?slice_id=<id>
I amb la consulta:
select d.dashboard_title , d.id from dashboard_slices ds join dashboards d on ds.dashboard_id =d.id join slices s on ds.slice_id =s.id where s.id=<chart id> order by dashboard_title
Podrem extreure els dashboards on es fa servir el gràfic.
Generació de dades dels quadres de comandament
Per a obtenir les dades dels quadres de comandament de Superset utilitzarem la consulta:
select dashboard_title, coalesce(description,'Sense descripció') as description, b.params
, d.id as dashboard_id, coalesce(slug, d.id::varchar)as slug, coalesce(certified_by,'') as certified_by
from dashboards d
left join (
select id as dashboard_id, string_agg(param, ' | ' order by param) as params
from (
select id,
replace((json_array_elements_text(json_metadata::json->'native_filter_configuration')::json->'name')::varchar, '\"', ' ' )
as param
from (select id, json_metadata from dashboards ) a
) b
group by id
) b on d.id=b.dashboard_id
order by dashboard_title
Que conté les dades dels quadres de comandament amb els seus filtres.
I amb la consulta:
with jsonval as (
select id, replace(replace(replace(replace(json_each(position_json::json)::varchar,'""', '"'),'"{','{') , '}")', '}'), '\\"', '') as val
from dashboards
)
, jsonval2 as (
select id
, ((substring(val, position('{' in val), length(val) )::json)->'meta')::json as val
from jsonval
where substring(val, position('{' in val), length(val) ) ilike '{"children%'
)
, chartval as (
select
replace((val->'sliceNameOverride')::varchar,'"', '') as sliceNameOverride
, val->'chartId' as chart_id
from jsonval2
where 1=1
and val->'chartId' is not null
)
select replace(s.slice_name,'\', '') as slice_name, s.id as slice_id , t.table_name
, replace(replace(replace(replace(replace(coalesce(cv.slicenameoverride,''), '\u00f3', 'ó'), '\u00e0', 'à'), '\u00f2', 'ò'),'\u00e9','é'),'\u00e8','è')
as slicenameoverride
from dashboard_slices ds
join dashboards d on ds.dashboard_id =d.id
join slices s on ds.slice_id =s.id
join tables t on s.datasource_id = t.id
left join chartval cv on cv.chart_id::varchar::int=s.id
where d.id=<dashboard id>
order by slice_name
Podrem llegir el json intern que té cada dashboard i aconseguir els seus charts.
Construcció del fitxer exposures.yml
A partir de les funcions muntades amb cada consulta, podem generar el fitxer exposures.yml.
Cal tenir molt en compte els tabuladors i salts de línia a l’hora de generar el fitxer.
Un cop construït el fitxer exposures.yml ja podem tornar a generar la documentació amb dbt docs generate , i tindrem de forma integrada la informació dels nostres processos ETL amb els elements de Superset, incloent el data lineage.
Paràmetres del procediment
Els paràmetres per a fer la crida al procediment de generació de documentació són:
- URL de la base de dades del servidor de Superset.
- Port de la base de dades del servidor de Superset.
- Nom de la base de dades del servidor de Superset.
- Usuari de la base de dades del servidor de Superset.
- Paraula de pas de la base de dades del servidor de Superset.
- Path on es generarà el fitxer d’exposures.yml.
- URL git del projecte.
- URL del fitxer schema.yml on hi ha la definició de les fonts de dadaes.
- URL del Superset.
Dins del servidor on tenim desplegada l’arquitectura amb DBT i Superset podem fer la crida de la següent forma:
A=`docker ps --filter "name=superset_db" -q`
B=`docker inspect --format '{{ index .NetworkSettings.Networks "superset_default" "IPAddress" }}' $A`
python generadoc_exposure_dbt.py $B 5432 superset superset superset "/home/bi/dbt/daily/models/exposures.yml" https://git.coopdevs.org https://git.coopdevs.org/coopdevs/bi/coopdevs/bi-coopdevs-scripts/-/raw/main/daily/models/schema.yml 'https://bi-sc.coopdevs.org'
docker exec -w /home/airflow/dbt/daily -it airflow_airflow-worker_1 dbt docs generate
rm -rf /home/bi/docs/docs/target
docker cp airflow_airflow-worker_1:/home/airflow/dbt/daily/target /home/bi/docs/docs/target
Limitacions
Com ja hem vist a l’inici, DBT limita molt els tipus que es poden posar a dins de les exposures i hem hagut de fer una correspondència entre el que permet DBT i els elements de Superset. A més, a l’hora de muntar el lineage, DBT no permet relacions entre exposures, només entre elements dels projectes, amb el que tots els quadres de comandament, gràfics i datasets penjaran de la mateixa taula del model. Tot i això, des de la descripció dels elements de Superset podrem anar navegant entre ells.
És molt important mantenir la documentació interna de Superset actualitzada, com pot ser la descripció dels diferents elements, perquè així el procés de generació podrà alimentar-se d’aquesta informació.
Com sempre, podeu trobar tot el codi al nostre git: https://git.coopdevs.org/coopdevs/bi/generador-documentacio-superset