MetadataSCALE-SPECIFICORG-SPECIFICTOOL-SPECIFIC

Data Discovery

How someone finds the right dataset among hundreds. Search over descriptions fails; search over what people actually query works.

What actually happensHow to build itCan I trust it?

Who needs this, what one row is, and why the obvious build breaks

Every lesson starts from the consumer, because designing from the source outward is this domain's characteristic mistake.

The question

A new analyst needs quarterly revenue by country. How do they find the right table among four hundred, and not the plausible wrong one?

Who needs this

Someone who does not know what exists, cannot evaluate what they find, and has a deadline. They will pick something within about two minutes and build on it, so the ranking decides the answer far more than the content does (The Data Catalog).

What one row is

The unit being ranked is a dataset a consumer could query, scored by evidence of trustworthy use. Scoring at column granularity is what makes "which table has customer revenue" answerable at all, since the word "revenue" appears in a column far more often than in a table name (Column-Level Lineage).

The obvious build

Put a search box over the catalog and match the query against table names and descriptions. It is the obvious implementation, it is what every catalog ships with, and it works well for the person who already knows the table name.

Why it breaks

Searching "revenue" returns forty tables. Most have no description, so the ranking falls back to name similarity and the shortest, oldest, most deprecated name wins (The Data Catalog).

How it breaks with real data
  • Searching "revenue" returns forty tables. Most have no description, so the ranking falls back to name similarity and the shortest, oldest, most deprecated name wins (The Data Catalog).
  • The best-modelled table is called fct_orders and contains a revenue column. It does not rank for "revenue" at all, because nothing indexed the columns.
  • Descriptions that do exist were written by the producer in producer language — "order fact at line grain, post-allocation" — and the searcher typed the words a business user uses. Vocabulary mismatch is the normal case, not the exception.
  • A staging model outranks the curated mart because its name is a closer string match, and nothing in the ranking knows that one is a production interface and the other is scaffolding (Model Layering).
  • The analyst gives up and asks in a chat channel, which works, and is why nobody notices the search is failing. The failure is invisible because the workaround is social (The Data Catalog).
SourceIngestionRawTransformationValidationStorage ModelServingConsumerObservability

What is actually happening

  • Discovery is a ranking problem, not a storage problem. The candidate set is easy — a text match over names, columns and descriptions produces it in milliseconds. Everything that decides whether the tool works happens in the ordering (Linear Search).
  • The reason description search fails is that descriptions are the one signal in the system that is optional, written by the wrong person, and never updated. Ranking on them means ranking on the sparsest and least reliable field available (Dataset Documentation).
  • The reason usage search works is that querying is not optional. Every SELECT in the warehouse is a costly vote cast by someone who had a reason, and the query log records all of them without anyone deciding to participate (Follow the Query).
  • The strongest single signal is distinct recent readers, not query count. Query count is dominated by scheduled jobs, which reflect what was built rather than what is trusted; distinct humans reflects what people choose when they have alternatives (Cardinality: The Label That Took Down Monitoring).
  • The second strongest is downstream position: a dataset feeding several dashboards is more likely to be the intended interface than one feeding nothing, and that fact comes from lineage rather than from anything a human wrote (Data Lineage).
  • The two failure directions are worth naming. Ranking purely by text finds the plausible name; ranking purely by usage entrenches whatever is popular, including a popular mistake. A usable ranking uses text for recall and evidence for order, with deprecation as a hard demotion rather than a weight (Deprecation as a Process, Not a Label).

Why searching the descriptions fails

It is worth being precise about why the obvious implementation disappoints, because the usual conclusion — "people need to write better descriptions" — leads to a campaign that does not fix it.

Descriptions fail as a ranking signal for three independent reasons and any one would be sufficient. They are optional, so they are absent on most of the corpus. They are written by producers, so they use producer vocabulary while searches use consumer vocabulary. And they are never updated, so where they exist they describe a previous version of the model (Dataset Documentation).

Usage has none of those properties. Nobody opts into being counted, the vocabulary question does not arise because no words are involved, and the signal regenerates continuously — a table that stops being useful stops being queried, and its rank falls without anyone deciding to demote it.

Rank by text match over names and descriptions
Score each dataset by string similarity between the query and its name, description and tags. Return the top matches. Where descriptions are missing, fall back to name similarity, which in practice means the shortest name containing the search term wins.
Match on text, order by evidence of trustworthy use
Use text over names, column names, descriptions and tags to build the candidate set, then order by downstream dashboard count, distinct human readers in a full reporting cycle, and freshness against SLO — with deprecated and staging datasets structurally demoted below everything current regardless of score.

Text tells you which datasets mention the term; it cannot tell you which one an organisation has decided to rely on. Usage and downstream position are that decision, recorded continuously and costlessly by people who had something at stake — which makes them both more available and more honest than any field a producer was asked to fill in.

search: "revenue"

  text-only ranking                       evidence ranking
  ────────────────────────────────        ────────────────────────────────
  1. revenue          (deprecated)        1. revenue_daily      12 readers, 4 dashboards
  2. revenue_v2       (0 readers)         2. fct_orders          9 readers, 3 dashboards
  3. revenue_daily                        3. revenue_v2          1 reader, 0 dashboards
  4. tmp_revenue_fix  (1 reader)          4. revenue          [deprecated, demoted]
  5. fct_orders       (no name match)     5. tmp_revenue_fix     1 reader, 0 dashboards
                       ^ ranks last              ^ ranks 2nd via its 'revenue' column

Ranking on what people actually query

The query below is the whole idea, and it is deliberately unremarkable: aggregate the warehouse's own query history into per-dataset usage, join it to the catalog and to downstream lineage counts, and order by evidence.

Two details carry most of the value. Counting distinct readers rather than queries keeps scheduled jobs from dominating — a pipeline that reads a table hourly is one dependency, not a thousand endorsements. And filtering to human identities, where the warehouse allows the distinction, separates what people choose from what was built.

The nulls last on the final ordering matters more than it looks. Datasets with no usage at all are not errors and should not be excluded — a newly published replacement model has no readers by definition, and dropping it from results is exactly how a platform prevents its own migrations (Deprecation as a Process, Not a Label).

Ranking candidates by evidence rather than by prose
1-- :since should span a full reporting cycle, or quarterly consumers vanish.
2with reads as (
3 select referenced_table, user_identity, query_date
4 from query_log
5 where query_date >= :since
6 and statement_type = 'SELECT'
7 and identity_type = 'USER' -- exclude scheduled service accounts
8),
9usage as (
10 select referenced_table,
11 count(distinct user_identity) as distinct_readers,
12 max(query_date) as last_read
13 from reads
14 group by referenced_table
15)
16select c.qualified_name,
17 c.owner_team,
18 c.lifecycle, -- 'current' | 'staging' | 'deprecated'
19 coalesce(d.downstream_dashboards, 0) as dashboards,
20 coalesce(u.distinct_readers, 0) as readers,
21 u.last_read
22from catalog_tables c
23left join usage u on u.referenced_table = c.qualified_name
24left join lineage_downstream_counts d on d.qualified_name = c.qualified_name
25where c.qualified_name like :term
26 or c.description like :term
27 or exists (select 1
28 from catalog_columns k
29 where k.qualified_name = c.qualified_name
30 and k.column_name like :term)
31order by
32 case c.lifecycle when 'deprecated' then 2 when 'staging' then 1 else 0 end,
33 dashboards desc,
34 readers desc,
35 u.last_read desc nulls last

The lifecycle sort key comes first and is a hard demotion, not a weight. That ordering is what stops a popular deprecated table outranking its correct replacement for the entire migration period — a scoring model with a deprecation penalty always loses that fight eventually, because usage is a much larger number than any penalty anyone is willing to tune.

Product detail — verify current documentation

Every warehouse exposes query history somewhere, under a different name, with different retention and different fidelity about the identity behind a query — and retention in particular is a setting that changes. Verify current documentation for what your engine records, how long it keeps it, and whether reads through a shared account can be attributed at all.

The signals, and what each one is blind to

SCALE-SPECIFICThe ordering power column reflects a platform with hundreds to thousands of datasets and a mixed analyst population. Below that, browsing beats every signal here; well above it, recall problems dominate and column indexing plus synonym handling matter more than any reordering of these rows.

No single ranking signal is sufficient, and the useful way to think about a discovery system is as a small portfolio with a stated blind spot per component — the same structure this domain uses for data quality (Data Quality).

The table below ranks the signals by how much ordering power they carry in a typical warehouse, with the blind spot for each. Note that the two most valuable signals are both free by-products of the platform operating, and the two weakest are the ones organisations run campaigns about.

The last row is the one people forget. A dataset can rank first on every behavioural signal and still be the wrong answer for this particular searcher, because their question has a grain the dataset cannot serve. Ranking gets someone to a plausible table; only documentation of grain stops them using it wrongly (Grain: What Does One Row Represent?).

SignalWhere it comes fromOrdering powerBlind to
Downstream dashboard and model countLineage graphHighest — it encodes what the organisation already decided to depend on.Newly published datasets, which have no downstream yet and are exactly what a migration needs promoted (Data Lineage).
Distinct human readers in the windowQuery history, filtered to user identitiesHigh — a costly, voluntary vote by someone with alternatives.Platforms where reads arrive through a shared service account, which collapses the count to one for everything.
Lifecycle state (current / staging / deprecated)Authored, one enumerated fieldHigh as a hard demotion, weak as a score.Datasets that are effectively deprecated and never marked, which is most of them (Deprecation as a Process, Not a Label).
Freshness against declared SLOComputed per datasetModerate — it removes results that are stale rather than reordering the live ones.Fresh data that is wrong. Freshness and correctness are independent properties (Freshness Checks).
Column-name matchHarvested schemaModerate for recall, weak for order — it finds tables no name match would.Common columns: every table has created_at, so unweighted column matches flood the candidate set (Metadata: Technical, Operational and Business).
Description text matchAuthored, optional, rarely updatedLow — sparse on the corpus and written in producer vocabulary.Everything that has no description, which is the majority, and every consumer whose words differ from the producer's (Dataset Documentation).
Grain compatibility with the question askedAuthored, and almost never modelledNot usually implemented at all.Nothing — this is the signal that would actually prevent misuse, and no behavioural proxy substitutes for it (Grain: What Does One Row Represent?).

How to build it

Most important first.

  • Index columns, not just tables. The business word a searcher types is far more likely to be a column name than a table name, and this single change usually improves recall more than any amount of description writing (Metadata: Technical, Operational and Business).
  • Rank by distinct recent readers and downstream consumer count before text relevance. Text decides the candidate set; evidence decides the order (Data Lineage).
  • Demote deprecated and staging datasets structurally rather than by score. A deprecated model should be visibly marked and pushed below everything current, no matter how good its text match (Model Layering).
  • Show the evidence on the result row: how many people queried it recently, which dashboards read it, when it last updated, whether its checks pass. A searcher can evaluate a result they can see the basis for (The Data Quality Dashboard).
  • Capture zero-result and abandoned searches. They are a free, continuously updated list of what the platform is missing or has named unfindably, and it costs nothing to collect (The Data Catalog).
  • Support "who else uses this" as a first-class action. The fastest way for an analyst to validate a table is to see that three people they trust query it weekly, and that is a lineage-and-usage query rather than a documentation one.

What this actually promises

Naming the guarantee you do not have is worth more than naming the one you do — everything downstream inherits the weakest promise in the chain.

  • Search guarantees recall only over indexed fields. A dataset with no description and unindexed columns is findable by exact name and by nothing else, which for a new joiner means not findable (The Data Catalog).
  • Usage ranking guarantees a reflection of what people queried in the window observed. It does not guarantee they were right — a widely used table with a known flaw ranks first, and popularity is evidence rather than proof.
  • Nothing guarantees the searcher understands the result. Discovery ends at "this is probably the right table"; whether they use it correctly is a documentation and grain problem (Grain: What Does One Row Represent?).
  • Query-log-derived signals are bounded by log retention and by whether reads through a shared service account can be attributed to individuals. Where they cannot, distinct-reader counts collapse and the strongest signal is lost.

Can I trust it?

A green pipeline is evidence that code ran. These four fields are the evidence that the data is right.

The check that would catch this
  • Measure search success, not search volume: the share of searches followed by the searcher querying one of the top results, and the share ending in a chat-channel question instead. The second number is the honest one and requires actually looking.
  • Keep the zero-result query list and review it. Every entry is either a missing dataset, a naming problem, or a vocabulary gap between producers and consumers, and all three are actionable (One Vocabulary: Naming and Consistency).
  • Both miss the searcher who confidently picked the wrong table and never came back. That failure produces a successful-looking search and a wrong dashboard, and it is only ever discovered downstream (Two Dashboards, Two Numbers).
Freshness
  • The usage window has to be long enough to represent periodic consumers and short enough to reflect current practice. Too short and quarterly work disappears; too long and a migrated-away table keeps its rank for months (Impact Analysis).
  • Index lag is felt directly: a dataset that exists and is catalogued but not yet indexed reads to the user as absent, and they conclude the catalog is incomplete rather than that it is behind.
  • Freshness of the *result* matters as much as freshness of the index. Showing when a dataset last updated on the result row is what stops someone building on a table that stopped being written three weeks ago (Freshness Monitoring).
When the schema or meaning changes
  • When a dataset is superseded, its usage signal decays slowly while the replacement builds up. During that overlap the old one still outranks the new one, which is why deprecation must be a hard demotion rather than a weight (Deprecation as a Process, Not a Label).
  • Renames reset usage history if datasets are keyed by name, so a renamed table drops out of the rankings and looks abandoned. Stable identifiers are as important for discovery as they are for lineage (The Data Catalog).
  • As the platform grows, the vocabulary gap widens: producers name things after processes, consumers search for outcomes. Synonym handling stops being a nicety and becomes the difference between recall and none.
How to re-run this safely
  • The index is derived and rebuildable from the catalog and the query log. Treat it as a cache with a rebuild path, not as a store (The Data Catalog).
  • When ranking is discovered to be entrenching a wrong table, the fix is a demotion applied to the specific dataset plus a redirect to the replacement — not a ranking model change, which will take a quarter and break other things.
  • Usage history lost through a rename can be recovered by mapping the old identifier to the new one in the log-derived aggregation, provided the mapping was recorded at rename time. It cannot be reconstructed afterwards.

What can go wrong

Failure modes
  • Ranking by text similarity only, which reliably promotes short, old, deprecated names over well-modelled current ones.
  • A description-quality campaign launched to fix search, which improves the field nobody ranks on and leaves the ordering unchanged (Dataset Documentation).
  • Usage signals dominated by scheduled jobs, so the top result is whatever the orchestrator reads most rather than what humans trust.
  • Reads arriving through a shared service account, collapsing distinct-reader counts to one and removing the strongest available signal.
  • Search that succeeds and misleads: the searcher finds a plausible table quickly, uses it wrongly, and the tool records a successful session (Grain: What Does One Row Represent?).
Misreads
  • "Search is failing because descriptions are missing." Descriptions are the sparsest and least reliable field in the system. Ranking on evidence of use fixes more, faster, and does not depend on anyone writing anything (Dataset Documentation).
  • "Nobody uses the catalog." Measure where they go instead. If the answer is a chat channel, the catalog is not unused — it is losing to a better-ranked human (The Data Catalog).
  • "Popular means correct." It means popular. A widely used table with a known caveat outranks its own corrected replacement for as long as the ranking is purely behavioural, which is why deprecation must override the score (Deprecation as a Process, Not a Label).
  • "We need better search technology." The retrieval is rarely the problem; the ranking signals are. A basic text match over columns, ordered by distinct readers, outperforms a sophisticated matcher ranking on empty descriptions (Metadata Filtering).
Privacy, retention and access
  • Query history is the strongest discovery signal and is also a record of who read what. Using it for ranking is legitimate and should be a stated use, because staff can reasonably ask why their query history is being processed (Audit Logs for Privileged Actions).
  • Search results should respect access: showing an analyst a dataset they cannot read is usually correct and showing its column names and sample values may not be, since column names alone can disclose sensitive structure (Data Access Control).
  • Discovery over datasets containing personal data should surface the classification prominently, so the decision to request access is made with the obligation visible rather than discovered later (Data Classification).

Operating it

How you see it in production
  • Share of searches where the searcher subsequently queried a top-ranked result, trended. It is the closest thing to a correctness metric discovery has.
  • Zero-result searches, as a reviewed list rather than a count (The Data Catalog).
  • The gap between datasets that are queried and datasets that are findable: tables with real usage that do not rank for any obvious business term are the platform's naming debt (One Vocabulary: Naming and Consistency).
  • Distinct human readers per dataset, which doubles as the deprecation input — datasets with no human readers in a long window are candidates for removal (Deprecation as a Process, Not a Label).
What changes at 10x and 100x
  • Below a hundred datasets, browsing works and ranking is irrelevant. A well-named schema and a README outperform any search product.
  • At a thousand, ranking is the product and text-only ranking is actively harmful because the candidate set is always large.
  • At ten thousand, discovery needs the same treatment as any retrieval system: recall and precision measured separately, synonym handling, and the acceptance that no single signal suffices (RAG Overview).
What drives cost here
  • Index build cost is small and scales with object and column count. Column indexing multiplies the entry count substantially and is still cheap relative to anything that scans data (What Actually Drives Data Platform Cost).
  • Computing usage signals means aggregating the query log, which is a genuine analytical workload over a high-volume table and should be pre-aggregated on a schedule rather than computed per search (Scan Cost).
  • The cost avoided is duplicated work: an analyst who cannot find the existing revenue model builds a second one, and the platform now pays to compute, store and reconcile two (Compute Waste).
What this approach costs
  • Ranking by usage is accurate about what people trust and conservative about anything new. A correctly built replacement model starts with no usage and therefore no rank, so new datasets need an explicit promotion path or they never get adopted.
  • Indexing columns improves recall and floods results for common words — every table has a created_at — so column matches need to be weighted below table and description matches rather than merged with them.
  • Exposing query history as a ranking signal makes discovery work and makes who-queried-what visible, which is a monitoring capability with its own governance question (Data Access Control).

Where this applies

Almost nothing here is universal. These labels say what each claim is specific to, and where a different engine, format, warehouse or scale would differ.

  • SCALE-SPECIFICRanking is irrelevant below roughly a hundred datasets, where a naming convention and browsing win outright, and becomes the entire product above a thousand, where the candidate set for any business term is always too large to read. Advice that does not state which regime it addresses is untestable.
  • ORG-SPECIFICThe vocabulary gap between producer names and consumer searches widens with the number of teams, because each team names things after its own process. In a single-team platform the names and the searches come from the same vocabulary and search works with no ranking at all.
  • TOOL-SPECIFICWhether usage signals are available at all depends on the warehouse exposing query history with an attributable identity. Where reads arrive through a shared service account, distinct-reader ranking is unavailable and downstream lineage position becomes the strongest remaining signal.

Where the depth lives

This domain teaches how data moves and how you know it arrived intact. It hands the rest off by name.

Domains that do not exist yet
  • DevOps / Production Engineering owns the equivalent problem for services — how an engineer finds the right service, its owner and its runbook — and the ranking argument transfers directly: usage and dependency position beat descriptions there too.