-
Notifications
You must be signed in to change notification settings - Fork 783
mdcode: add Knowledge Catalog pull for the semantic model #277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d8a4c63
2d7f9bf
da163dc
5b41f64
b0dcf57
e1982e6
e6d5e1f
efcc162
ff04848
8768a40
b36b758
abc370b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,8 +13,9 @@ model to two destinations at once: | |
| Both are generated from the same source document — you never author them | ||
| separately, and a single `push` keeps them in sync. | ||
|
|
||
| This guide covers authoring, deploying, and updating a model. For the Ossie | ||
| document format itself, see [ossie.apache.org](https://ossie.apache.org/). | ||
| This guide covers authoring, deploying, pulling back, and updating a model. | ||
| For the Ossie document format itself, see | ||
| [ossie.apache.org](https://ossie.apache.org/). | ||
|
|
||
| ## Prerequisites | ||
|
|
||
|
|
@@ -137,6 +138,12 @@ statement runs in the right region; without that permission it falls back to | |
| BigQuery's own location inference and warns. Nothing runs under | ||
| `--validate-only`; add `--print` to see the DDL. | ||
|
|
||
| Push to BigQuery is **lossy**: the graph captures the queryable structure — | ||
| node tables, edge tables, and measures — but not descriptive metadata | ||
| (descriptions, `ai_context`, synonyms, labels), and a metric that does not | ||
| reduce to a single MEASURE is dropped with a warning. It is a query surface, | ||
| not a copy of your model. | ||
|
|
||
| ### What gets created in Knowledge Catalog | ||
|
|
||
| Each element of your model maps to one catalog resource. Every resource type | ||
|
|
@@ -154,23 +161,35 @@ An entity entry carries its columns in the `schema` aspect (name, data type, and | |
| description per field); a `schema-join` link carries the relationship detail — the | ||
| paired columns and foreign-key direction — in its aspect. | ||
|
|
||
| > **Note — the catalog is not a full copy of your model.** By default the SQL | ||
| > expressions are **not** written to Knowledge Catalog: the published system-type | ||
| > templates do not yet carry a per-field `semantics` block or a | ||
| > `semantic-metric.expression` field, so the default push omits them (pass | ||
| > `--emit-expressions` to write them once the templates gain the fields). The | ||
| > original vendor SQL (`importedExpression` — e.g. the MAQL or Snowflake form a | ||
| > metric was imported from) is never written either. All of it stays in your | ||
| > authored document and is still used when generating BigQuery SQL. Keep your | ||
| > model document as the source of truth: a model reconstructed only from the | ||
| > catalog would come back without its SQL. | ||
| > **Note — push to Knowledge Catalog is lossy.** The catalog holds metadata, | ||
| > not a full copy of your model. It **stores** names, descriptions, data | ||
| > sources, field datatypes and roles, and 1:1 / 1:N relationships (as | ||
| > `schema-join` links). By default it does **not** store the SQL expressions: | ||
| > the published system-type templates do not yet carry a per-field `semantics` | ||
| > block or a `semantic-metric.expression` field, so the default push omits them | ||
| > (pass `--emit-expressions` to write the canonical GoogleSQL/ANSI expression | ||
| > once the templates gain the fields). It never stores entity keys, `ai_context`, | ||
| > field labels, the original vendor SQL (`importedExpression` — e.g. the MAQL or | ||
| > Snowflake form a metric was imported from), or M:N relationships. Those stay in | ||
| > your authored document (and, for the edges, in the BigQuery property graph); the | ||
| > vendor SQL and expressions are still used when generating BigQuery SQL. Keep | ||
| > your model document as the source of truth. | ||
|
|
||
| ## Validation | ||
|
|
||
| `push` and `--validate-only` run the same checks, **before either destination is | ||
| touched**, so a model that cannot deploy fails fast instead of half-deploying: | ||
|
|
||
| * **Exactly one deployment target per model.** *(static)* | ||
| * **Exactly one deployment target per model, and it must be a valid BigQuery | ||
| Graph URI.** A model with no target — or with more than one — is rejected, and | ||
| so is a single target whose URI does not match | ||
| `//bigquery.googleapis.com/projects/<p>/datasets/<d>/propertyGraphs/<g>` (for | ||
| example a `propertyGraph`/`propertyGraphs` typo, or a | ||
| `…/entryGroups/@bigquery/entries/…` entry form). The error names the offending | ||
| URI and the expected form. This gate runs before any destination leg and for | ||
| every `--target`, so a malformed target writes **nothing** — not to BigQuery | ||
| and **not to Knowledge Catalog**; the push aborts with a non-zero exit and no | ||
| entries are created. *(static)* | ||
| * **Every metric on a BigQuery Graph model resolves to exactly one entity** — | ||
| otherwise it would be dropped from the BigQuery Graph. Set the metric's attach | ||
| entity, or scope its expression to a single entity. *(static)* | ||
|
|
@@ -220,3 +239,109 @@ Every push prints one line per destination summarizing what it did. For a | |
| Deployed 1 BigQuery Graph(s). | ||
| Wrote 5 new and 2 updated Knowledge Catalog entries; removed 1 orphaned entry; linked 2 relationships; unlinked 1 orphaned link. | ||
| ``` | ||
|
|
||
| ## Pull | ||
|
|
||
| `kcmd pull` is the inverse of push's Knowledge Catalog leg: it reads the | ||
| `semantic-*` entries back from the catalog and reconstructs local model | ||
| documents at `catalog/EntryGroups/<entryGroupId>/<model>.yaml`. Use it to | ||
| recover a workspace from a catalog someone else deployed, or to see what the | ||
| catalog actually holds. | ||
|
|
||
| ```bash | ||
| kcmd pull | ||
| ``` | ||
|
|
||
| Pull reads only from Knowledge Catalog (never BigQuery). Its coordinates come | ||
| from the same scope you authored under (`<projectId>.<locationId>.<entryGroupId>`). | ||
|
|
||
| | Flag | Effect | | ||
| |------|--------| | ||
| | `--dry-run` | Reconstruct from the catalog and report what would be written, but write no files. | | ||
| | `--model <name>` | Pull a single model by name; other models in the entry group are left alone. | | ||
|
|
||
| One entry group can hold **many models** — each `semantic-model` entry is a | ||
| separate anchor, and pull reconstructs one document per anchor. `--model` | ||
| narrows both the fetch and the write to a single anchor. | ||
|
|
||
| Pull writes with the same last-write-wins policy as the core pull: a model that | ||
| already exists locally is overwritten in place, and a local-only document (one | ||
| with no matching catalog entry) is left untouched — pull never deletes. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "pull never deletes" --- can we fail pull by default if the id of the model available in the entryGroup that we're pulling from is different from the id of the model available locally on disk? if user wants to proceed then they'll need to explicitly use the new --force-remove flag we already support in push. If force-remove is specified then pull will erase the existing model and will pull the model from the KC. |
||
|
|
||
| > **Note — pull reconstructs what the catalog holds, not your original file.** | ||
| > Pull can only recover what push wrote (see the note under [What gets created in | ||
| > Knowledge Catalog](#what-gets-created-in-knowledge-catalog)). What that means in | ||
| > practice: | ||
| > | ||
| > **Recovered exactly** — these come back as authored: | ||
| > - Model structure: the model, its entities, and each entity's fields. | ||
| > - Field data source and data type. | ||
| > - Metrics: name, data type, and attach entity. | ||
| > - 1:1 / 1:N relationships: endpoints, foreign-key direction, and join columns | ||
| > (from the `schema-join` links). | ||
| > - Deployment targets. | ||
| > | ||
| > **Recovered only if pushed with `--emit-expressions`** — the per-field | ||
| > `semantics` block (expressions and the dimension role) and the metric | ||
| > expression are omitted from the catalog by default (see the note above), so | ||
| > pull returns them only when the push that wrote them used `--emit-expressions`: | ||
| > - Field expressions and metric expressions (the canonical GoogleSQL/ANSI form). | ||
| > - A field's dimension role, which comes back as a bare `dimension: {}` marker, | ||
| > without its detail (`is_time`, and so on). A default push drops the marker | ||
| > entirely. | ||
| > | ||
| > **Recovered, but normalized** — the content survives, the form changes: | ||
| > - Relationship *names* come back lowercased/hyphenated (the catalog stores the | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should also mention the order of the items emitted by pull. Field order within each entity will be preserved, but the order of datasets, metrics won't be. Let's also say that any comments in the original Yaml file are not preserved either. |
||
| > name only in the link id, e.g. `Places Order` → `places-order`). | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we change relationship name on push then kcmd push should warn user about name change. |
||
| > - A metric authored with no data type comes back as an explicit `Decimal` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use 'Opaque' type instead (for both fields and metrics) if type is not provided by the user. |
||
| > (push must write a type, and defaults it to `NUMERIC`). | ||
| > | ||
| > **Not recovered** — push never wrote these, so pull cannot return them: | ||
| > - Entity keys / unique keys. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pk/unique keys should be persisted in the schema aspect. let's add their support in a follow up PR.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed — tracked as a follow-up (persist pk/unique keys in the schema aspect). Listed under "Deferred" in #298; not in this follow-up. |
||
| > - `ai_context`. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should go to the guidelines aspect. see design doc.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed — tracked as a follow-up ( |
||
| > - Field labels. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. schema aspect support 'annotations' map for a field. perhaps we should put label there:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed — tracked as a follow-up (field label → schema aspect |
||
| > - The original vendor SQL (`importedExpression`). | ||
| > - M:N relationships (the edge lives only in the BigQuery property graph). | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit. let's remove M:N, since Ossie doesn't have those yet |
||
| > | ||
| > **So: a push followed by a pull does not return your original file.** Treat a | ||
| > pulled document as a faithful copy of the catalog metadata, not of the authored | ||
| > model, and keep the authored document as the source of truth. | ||
|
|
||
| > **Note — writer-side follow-up (not inherent to pull).** One reduction above is | ||
| > a limit of what push currently *writes*, not of what pull can recover. It is | ||
| > recorded here as a write-side follow-up; the reader (pull) already returns | ||
| > everything the catalog holds. | ||
| > | ||
| > - **Relationship names.** The `schema-join` aspect type's `metadataTemplate` has | ||
| > no field for the relationship name, so push cannot store it and pull recovers | ||
| > it from the link id — which is lowercased and hyphenated (the entry-link id | ||
| > format forbids the original casing/underscores). Returning the name verbatim | ||
| > requires adding a name field to the built-in `schema-join` aspect type in | ||
| > Knowledge Catalog (server-side), after which the client write/read is trivial; | ||
| > it is the same class of gap as the `semantics` field that gates | ||
| > `--emit-expressions`. | ||
| > | ||
| > (A non-canonical deployment target is **not** a pull gap: push rejects it at the | ||
| > validation gate before any leg runs, so it is never written — see | ||
| > [Validation](#validation).) | ||
|
|
||
| ## Permissions | ||
|
|
||
| `push` needs access to whichever destinations you deploy to. | ||
|
|
||
| **BigQuery** — for `--target bq` or `all`, and for the validation pre-flight: | ||
|
|
||
| * `bigquery.jobs.create` in the deployment-target project — to run the deploy's | ||
| `CREATE OR REPLACE PROPERTY GRAPH` and the validation dry-run query | ||
| * read access on each entity's source table, so the dry-run can resolve it | ||
| * `bigquery.datasets.get` on the target dataset (region detection; optional — | ||
| push degrades gracefully without it) | ||
|
|
||
| **Knowledge Catalog / Dataplex** — for `--target kc` or `all`: | ||
|
|
||
| * `dataplex.entryGroups.useSemanticModelAspect` on the destination entry group | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think dataplex.entryGroups.useSemanticEntityAspect and dataplex.entryGroups.useSemanticMetricAspect will also be required. |
||
| * `dataplex.entryGroups.useSchemaJoinEntryLink` and | ||
| `dataplex.entryGroups.useSchemaJoinAspect` when the model has relationships | ||
|
|
||
| `kcmd pull` needs read access to the same entry group instead — to list its | ||
| entries and fetch each `semantic-*` entry with its aspects. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for now we assume there's only on model in the entry group. We don't need --model flag