Zum Inhalt springen

Migration von Looker (LookML)

Der LookML-Importer lädt Looker-LookML-Modelle als Mondrian-4-Schemas in Saiku, sodass Sie OLAP/MDX darauf ausführen können. Er ist ein Migrationsbeschleuniger mit hartem Sicherheits-Gate, kein verlustfreier Konverter – jedes Konstrukt wird in eines von drei Urteilen klassifiziert:

  • CLEAN – auf ein Mondrian-Element portiert.
  • DEGRADE – portiert, aber eine Fähigkeit ging verloren (und wird präzise benannt).
  • REFUSE – nicht portiert, mit einer exakten Diagnose des Warum.

Wie es funktioniert

Der Importer ist eine vierstufige Pipeline:

  1. Parse – die .lkml-Dateien werden in ein Modell eingelesen (ein eingebauter, gehärteter LookML-Parser).
  2. Classify – ein statisches Sicherheits-Gate markiert jeden Explore und jedes Feld als CLEAN / DEGRADE / REFUSE, ohne Warehouse-Zugriff.
  3. Transpile – ein Mondrian-4-Schema für die CLEAN/DEGRADE-Untermenge ausgeben (als YAML, direkt von Saiku ladbar), plus eine Provenance-Map, welches LookML-Feld welches Schema-Element erzeugt hat.
  4. Report – ein Coverage-Report (Markdown + JSON), der jedes Konstrukt einteilt, mit zusammenfassenden Quoten.

Nur Konstrukte, die das Gate akzeptiert, erreichen jemals den Transpiler – eine abgelehnte, still-falsche Measure kann also niemals in den ausgegebenen Cube gelangen.

Den Importer ausführen

Der Importer wird als das Tool mondrian.lookml.report.LookmlReportCli bereitgestellt (auf dieselbe Weise, wie die Schema-CLI SchemaCli umschließt). Richten Sie ihn auf eine einzelne .lkml-Datei oder ein ganzes Projektverzeichnis – Verzeichnisse werden rekursiv nach .lkml-Dateien durchsucht.

Terminal-Fenster
lookml-report report <path> [-o report.md] [--json report.json] [--fail-on-refuse]
FlagWirkung
(keiner)Schreibt den Markdown-Report nach stdout.
-o <file>Schreibt den Markdown-Report in eine Datei.
--json <file>Schreibt zusätzlich den maschinenlesbaren JSON-Report.
--fail-on-refuseGibt den Report trotzdem aus, beendet sich dann mit Nicht-Null, wenn etwas abgelehnt wurde – ein CI-Gate für “blockiere die Migration, bis die Refuse-Liste leer ist.”

Exit-Codes: 0 Erfolg · 1 ungültige Argumente · 2 der Pfad fehlt/ist nicht lesbar, nichts war parsebar, oder --fail-on-refuse hat eine Ablehnung gesehen.

Was der Report enthält

  • Zusammenfassende Metriken – CLEAN / DEGRADE / REFUSE-Zählungen und Prozentsätze auf beiden Explore- und Feldebenen. Dieses Verhältnis ist die Schlagzeilenzahl für die Migrationsbereitschaft.
  • Pro-Konstrukt-Buckets – jeder Explore und jedes Feld wird unter Clean / Degrade / Refuse mit einem präzisen Grund aufgeführt, mit dem erzeugten M4-Element (für clean/degrade) oder der verlorenen Fähigkeit und einem Link zum Saiku-Feature, das eine Ablehnung aufheben würde.
  • Unparseable / skipped files – vollständige Transparenz über alles, was nicht aufgenommen wurde.

Multi-Datei-Projekte

Wenn Sie den Importer auf ein Projekt-Verzeichnis richten, löst er das gesamte Projekt auf, nicht nur eine Datei:

  1. Jede .lkml wird rekursiv entdeckt und unabhängig geparst (nicht parsbare Dateien und *.dashboard.lkml werden aufgeführt, niemals fatal).
  2. Die parsbaren Top-Level-Objekte werden zusammengeführt, dann löst ein Flatten-Durchlauf Cross-File-Referenzen in ein einziges Modell auf, bevor klassifiziert wird:
    • include: – durch das Zusammenführen erfüllt;
    • extends: – die Basis wird kopiert, dann überschreiben die eigenen Eigenschaften des erweiternden Objekts;
    • Refinements (+view / +explore / +model) – über die Basis gelegt (Skalare überschreiben; dimension / measure / join werden nach Namen zusammengeführt);
    • @{constant} – wird aus constant:-Blöcken substituiert.

Dies behebt Fehlklassifizierungen, bei denen eine Measure oder ein Feld erst nach einem Refinement additiv, Liquid oder zeilengesichert wird – auf einem echten Projekt hat das Hunderte von Feldern von einer Literal-Text-Vermutung auf das korrekte Urteil verschoben.

Import aus einer Live-Looker-Instanz (Explore JSON)

Mit einer Looker-Instanz mit Zugangsdaten können Sie das rohe .lkml-Parsing überspringen und die bereits aufgelösten Metadaten eines Explores importieren:

Terminal-Fenster
lookml-report report --explore-json explore.json

Exportieren Sie das lookml_model_explore-JSON des Explores aus der Looker-API. Da Looker bereits alle extends/Refinements/Konstanten/Liquid angewendet hat, überspringt diese Eingabe den Flatten-Durchlauf und fließt direkt in dieselbe Klassifizierer → Transpiler → Report-Pipeline und produziert denselben Coverage-Report wie das äquivalente .lkml. Trade-Off: erfordert API-Zugriff und ist pro Explore, ergänzt also den Offline-Pfad (auf-ein-Git-Repo-zeigen) – statt ihn zu ersetzen.

Was portiert wird

Das meiste eines sauberen Stern-/Schneeflocken-LookML-Modells wird direkt konvertiert. Der Importer ordnet zu:

LookMLMondrian-4Urteil
explore (single-base Stern/Schneeflocke)<Cube> mit einer <MeasureGroup> und konformen DimensionenCLEAN
explore mit Join über mehrere Fakten-Basen (konform)ein <Cube> mit einer <MeasureGroup> pro Fakt-Basis über gemeinsame konforme DimensionenCLEAN
join: { relationship: many_to_one | one_to_one, type: left_outer }ForeignKeyLink (degenerierte Dims → FactLink)CLEAN
Bridge two-hop: Fact one_to_many (oder many_to_many) → Bridge-View → many_to_one-Dimension-View<BridgeLink> (full-count De-Dup) + die Dimension als konforme DimensionCLEAN
measure: { type: sum | count | min | max | average | count_distinct }<Measure> mit dem passenden AggregatorCLEAN
measure: { type: median | percentile }aggregator="median" / aggregator="percentile"DEGRADE – benötigt ein PERCENTILE_CONT-fähiges Backend
Gefilterte Measure (filters: Gleichheit, kein Liquid)berechneter MemberCLEAN
dimension (+ value_format / value_format_name, label, description)Attribut / Level (+ formatString – Looker-benannte Formate wie usd, percent_2, decimal_0 werden in Mondrian-Masken übersetzt; ein unbekanntes benanntes Format wird wörtlich mit einem DEGRADE-Hinweis behalten – plus caption, description)CLEAN
dimension: { type: tier } / dimension_group: { type: duration }native <Tier> / <Duration>CLEAN
parameter (bounded: getypt, allowed_values)<QueryParameter>CLEAN
measure: { type: sum_distinct | average_distinct }, sql_distinct_key löst auf eine Spalte in der eigenen View der Measure auf (inkl. nicht-Primärschlüssel)<Measure> mit Distinct-Granularität auf Measure-Ebene (distinctKeyColumn) – auf diesem Schlüssel dedupliziert vor dem Aggregieren; reduziert sich zu einer einfachen sum/avg, wenn der Schlüssel der Primärschlüssel der View istCLEAN
Bounded Liquid: {% parameter %}, {% condition %}, {{ _user_attributes['x'] }}<QueryParameter> / Predicate-Grant-BindungDEGRADE
access_filter auf einem modellierten Dimensions-SchlüsselRole-Member-GrantCLEAN
access_filter auf einer beliebigen Fact-SpaltePredicate-Grant-Rolle + gebundener ParameterDEGRADE
derived_tableeine SQL-gestützte physische Tabelle (<Query>)DEGRADE – Persistenzpolitik verloren
drill_fieldsDrillthrough-RETURN-Set, als Cube-Annotation getragen (M4 hat kein <DrillThrough>-Schema-Element – es ist eine Laufzeit-DRILLTHROUGH … RETURN-Anweisung)CLEAN
aggregate_table(nicht konvertiert – Saiku regeneriert Aggregate)DEGRADE

Eine sum/average-Measure, die über einen one_to_many-Join expandiert (fans out), wird (CLEAN) portiert, wenn die Basis-View einen Primärschlüssel deklariert, weil Saikus Fan-Out-sichere symmetrische Aggregation auf dieser Granularität dedupliziert. Ohne deklarierten Schlüssel wird sie abgelehnt, statt das Risiko des Doppelzählens einzugehen.

Many-to-Many-Joins (Bridge-Dimensionen). Der Importer erkennt den kanonischen LookML-Bridge-Two-Hop – ein Fact mit one_to_many (oder many_to_many) zu einer Bridge-View, die wiederum many_to_one zu einer Dimension-View joint – und mappt ihn auf einen Mondrian-<BridgeLink>, statt den Explore als nicht-Stern abzulehnen. Die durch den Bridge erreichte Dimension wird zu einer normalen konformen Dimension, und Measures auf dem Fact geben die deduplizierte (full-count) Summe zurück, nicht die expandierte. Ein Bridge wird nur ausgegeben, wenn jeder Hop sich auf einen einspaltigen Schlüssel reduziert und die Fact-View eine primary_key: yes-Granularität deklariert; ein zusammengesetzter/mehrdeutiger Join-Schlüssel oder ein Fact ohne Primärschlüssel wird abgelehnt gelassen, statt einen still-falschen Cube zu produzieren. Da LookML keine Allokationsgewichtung trägt, verwenden Bridges standardmäßig die full-count-Deduplizierung.

from:-aliasierte Joins. In LookML wird ein joined-Feld immer über den Join-Namen referenziert, während from: (oder view_name:) nur die zugrundeliegende physische View austauscht. Der Importer matcht den sql_on ${name.column} jedes Joins gegen den Join-Namen – löst die Spalten und Tabelle der Dimension aus der zugrundeliegenden View auf – sodass ein Join wie join: current_subscription_state { from: logical_subscriptions; sql_on: ${fact.fk} = ${current_subscription_state.id} } als konforme Dimension benannt nach dem Join CLEAN portiert. Zwei Joins, die from: auf dieselbe Basis-View setzen, werden zu zwei separaten konformen Dimensionen (z. B. account_csm und account_owner über einer account-Tabelle). Was noch degradiert (DEGRADE_JOIN_SQL_ON_UNPARSEABLE): ein sql_on, das keine einspaltige Gleichheit auf jeder Seite ist – Konstanten-/Metadaten-Joins (${meta.col} = 'literal') und zusammengesetzte oder Ausdrucks-Joins (mit AND verkettete mehrspaltige Schlüssel, coalesce(...), ::date-Casts) werden degradiert gehalten, statt auf einen einzelnen, still-falschen Schlüssel reduziert.

Was abgelehnt wird (und warum)

AbgelehntWarumWeg nach vorn
Berechnetes Liquid ({% if %} / Schleifen / assign / berechnete {{ }} in SQL)Zur Laufzeit generiertes SQL ist ein Korrektheits-/Sicherheitsloch, das wir nicht importieren werdenBounded {% parameter %} / {% condition %} / {{ _user_attributes['x'] }} portiert jetzt (DEGRADE)
Fan-Out sum/average ohne deklarierbare GranularitätWürde stillschweigend doppelt zählenFügen Sie eine primary_key: yes-Dimension auf der Basis-View hinzu
Nicht-Stern-Topologien: full_outer / cross-Joins oder ein many_to_many, dessen Bridge-Two-Hop nicht wiederhergestellt werden kann (zusammengesetzter/mehrdeutiger Join-Schlüssel oder die Fact-View hat keinen primary_key)Brechen strukturell / expandieren unkontrolliert, oder es gibt keine einspaltige Granularität, auf der dedupliziert werden kannFür ein Many-to-Many: Geben Sie der Fact-View einen primary_key: yes und einspaltige Join-Schlüssel, damit der Importer einen Bridge ausgeben kann (der wiederherstellbare Two-Hop portiert jetzt automatisch)
type: sum_distinct / average_distinct, dessen sql_distinct_key ein cross-view (${other_view.field}), Fremdschlüssel- oder Ausdrucks-Schlüssel istEine Distinct-Granularität auf Measure-Ebene kann nur auf einer Spalte in der eigenen Fact-View der Measure deduplizieren; ein Cross-View-Schlüssel braucht den Bridge-Two-HopModellieren Sie den Join mit einer Bridge-Dimension (Many-to-Many)
type: listKein multidimensionales Äquivalent

Mondrian++-Erweiterungen erweitern die Abdeckung

Saikus semantische Erweiterungen existieren teilweise, um die Refuse-Liste zu verkleinern – jede einzelne verwandelt eine frühere Ablehnung in einen sauberen Port:

  • Symmetrische (Fan-Out-sichere) Aggregation, Bridge-Dimensionen (Many-to-Many) und Distinct-Granularität auf Measure-Ebene – siehe Fortgeschritten.
  • median / percentile-Aggregatoren, native Tier / Duration-Dimensionstypen – siehe Dimensionen und Cubes und Measures.
  • Bounded Query-Context-Parameter und Predicate-basierte Zeilensicherheit – siehe Zugriffskontrolle.

Mit weiteren Erweiterungen klassifiziert dasselbe LookML-Modell sauberer – führen Sie den Report erneut aus, um zu sehen, wie sich die Quote verbessert.

Realweltabdeckung

Validiert gegen einen Korpus öffentlicher LookML-Projekte (offizielle Looker-Blocks plus Produktiv-Community-Modelle, ~900 .lkml-Dateien): ~99,8 % der in Scope befindlichen Dateien parsen, jedes Projekt produziert einen projektweiten Report, und über ~20.000 Felder in echten Modellen ist die Abdeckung ~97,6 % CLEAN, ~1,2 % DEGRADE, ~1,2 % REFUSE. Die verbleibenden Ablehnungen werden dominiert von echtem berechnetem Liquid (inhärent dynamisch – ein “wird per Design nicht portieren”, kein Bug).

Numerische Äquivalenz validieren

Der Coverage-Report sagt Ihnen, was portiert; er sagt Ihnen nicht, ob der konvertierte Cube die gleichen Zahlen wie Looker zurückgibt. Ein CLEAN-Port, der falsch summiert, ist das schlimmste Ergebnis – daher überprüft eine separate Äquivalenz-Harness den konvertierten Cube gegen eine Live-Looker-Instanz – das Migrations-Analogon zum engine-seitigen Calcite-Parity-Guard.

Gegeben eine Query-Spezifikation (ein Explore plus die Dimensions- und Measure-Felder), führt die Harness die Abfrage auf beide Arten aus und vergleicht die Ergebnisse:

  • Looker-SeitePOST /api/4.0/login gefolgt von /api/4.0/queries/run/json liefert die Zeilen als Orakel. Richten Sie es auf Ihre Instanz mit drei Einstellungen (System-Properties oder Umgebungsvariablen – niemals einchecken): LOOKER_BASE_URL, LOOKER_CLIENT_ID, LOOKER_CLIENT_SECRET (ein Looker-API3-Schlüssel). Ohne alle drei ist der Client inert und die Harness bleibt vollständig offline.
  • Saiku-Seite – dieselbe Spezifikation wird über die Provenance-Map des Transpilers in MDX über den konvertierten Cube umgeschrieben (Measures auf Spalten, Dimensionsebenen auf Zeilen). Felder, die der Importer nicht CLEAN konvertiert hat, werden als skipped aufgeführt und nie still verglichen.
  • Vergleich – Zeilen werden über ihr Dimensionsschlüssel-Tupel ausgerichtet und Measures werden innerhalb einer relativen Toleranz verglichen (Standard 1e-6). Divergenzen werden in ROW_COUNT, DIMENSION_SET oder MEASURE_VALUE kategorisiert und benennen nur das Feld und die Kategorie – niemals die zugrundeliegenden Werte (keine Daten in Logs). Ein sauberer Lauf meldet einen Match mit null Divergenzen.

Die Harness validiert auch die Zeilensicherheit: Ein durch einen Looker-access_filter eingeschränktes Ergebnis wird gegen den konvertierten Cube verglichen, der unter dem entsprechenden Rollen-Grant abgefragt wird, was bestätigt, dass die eingeschränkten Zahlen übereinstimmen.

Einschränkungen (v1)

  • Cross-Datei-Auflösung von include:, extends:/Refinements und @{}-Konstanten wird durch einen Flatten-Durchlauf behandelt (siehe Multi-Datei-Projekte); eine Referenz, deren Basis oder Konstante außerhalb der entdeckten Dateimenge liegt, wird als Diagnose berichtet und as-parsed belassen, statt aufgelöst. Für ein vollständig vor-aufgelöstes Modell verwenden Sie das Explore-JSON-Frontend.
  • Konforme Multi-Basis-Explores portieren in einen Cube mit einer <MeasureGroup> pro Fakt-Basis; v1 verbindet jede sekundäre Fakt-Gruppe nur mit den konformen Dimensionen ihrer eigenen sql_on-Schlüssel – Base-View-degenerierte Dimensionen und Cross-Fact copy/no_link-Verdrahtung werden noch nicht synthetisiert.
  • *.dashboard.lkml (YAML-strukturierte Looker-Dashboards) werden übersprungen – sie sind nicht Teil des Cube-Modells.

Siehe YAML-Schemas für das Format, das der Importer ausgibt, und die Schema-CLI zum Konvertieren oder Linten des Ergebnisses, bevor es in Saiku deployt wird.