Concept ยท OrionBelt® Analytics

GraphRAG Schema Discovery: vector search finds, graph search connects

How OrionBelt® Analytics hands a language model the five tables a question needs out of five hundred, with the joins between them taken from real keys.

Why a model cannot just read the schema

A harmonized lakehouse layer easily holds hundreds of tables and thousands of columns. Sending all of that to a language model with every question is slow and expensive, and it makes the answers worse: the model has to guess which of many similar tables is meant, and then guess how to join them.

GraphRAG in OrionBelt® Analytics (OBA) answers those two questions separately, with two different kinds of search:

Neither is enough alone. Vectors know the meaning but not the joins. The graph knows the joins but not the meaning.

How a table becomes a vector

When OBA discovers a schema, every table, column, relationship and view is written out as a short piece of text. A table's text holds its name split into words, its comment, each column's name and type, and a line such as relates to customers for each foreign key. A column's text holds its table and column name split into words, its type, its comment, and primary key identifier or references customers where that applies.

orders net amt DECIMAL          (the column orders.net_amt)
        |
        v   all-MiniLM-L6-v2, on the server
[-0.01, 0.00, 0.07, ...]         (384 numbers)

Each text is turned into 384 numbers by all-MiniLM-L6-v2, a small open sentence-embedding model (Apache-2.0, about 22 million parameters). It was trained so that texts with similar meaning land close together: "revenue", "sales" and "turnover" are neighbours, "order date" is far away. That is why a question about revenue can find a column called net_amt at all.

For installs that cannot use the model, a keyword-only TF-IDF backend is available. It matches words, not meaning, and that difference is large in practice: against columns named salesamount, unitcost and returnquantity, a question about the "most profitable products that get returned the most" shares no word with the schema at all, every element scores zero, and the ranking is just index order. The default model finds productname, returnquantity and salesamount for the same question.

An honest example: the question "revenue"

The scores below are real measurements with this model: how close each stored text is to the question "revenue", where 1.0 would mean the same meaning.

Stored elementCloseness to "revenue"
orders.net_amt with the business name "Net revenue"0.48
orders.ship_cost0.26
orders.net_amt with the comment "net amount"0.22
orders.net_amt, bare0.17
orders.order_date0.04

The lesson is a limitation worth knowing. The bare net_amt loses to an unrelated shipping cost, because the model has never seen the abbreviation. A comment saying "net amount" barely helps. The business name "Net revenue" makes it the clear winner. The model knows language, not your abbreviations.

That is what the naming step is for. suggest_semantic_names finds cryptic and abbreviated identifiers and proposes business names, apply_semantic_names writes them into the ontology, and the applied names are indexed as their own entries. The one-call retrieval described below searches them together with the tables and columns, resolves each matching name to its table or column, and ranks it with the ordinary hits. A result found this way says which business name matched. Before the name, "revenue" ranked ship_cost first; with net_amt named "Net revenue", orders.net_amt comes first. A name that fits tables in two schemas is left out rather than guessed. The same tool family lets the model index other business vocabulary ("profit", "churn") for a table or column with add_semantic_context.

Questions in another language

all-MiniLM-L6-v2 is trained on English. A German question such as "Umsatz" scores 0.06 against the English name "Net revenue": no match. For users who ask in a different language than the schema is named in, set GRAPHRAG_EMBEDDING_MODEL=multilingual. That selects paraphrase-multilingual-MiniLM-L12-v2 (Apache-2.0), which puts more than 50 languages into one space of 384 numbers. With it, "Umsatz" scores 0.40 against "Net revenue" and ranks it first.

It runs the same way as the default model, on the CPU through the ONNX runtime, in an 8-bit version of 118 MB. The Docker image contains both models, so either works without network access. Outside Docker it is downloaded once from a pinned revision, and each file is checked against a pinned SHA-256 checksum. If it cannot be loaded, the server warns and uses the default model. Within one language the two rank alike; the default is smaller and slightly sharper on English.

How the graph connects

The second half is a graph: tables are nodes, keys are edges. Edges come from three sources, ranked in this order:

SourceScopeMarked in the join path
Foreign keys the database declaresEvery session on the connectionNo extra fields
Relationships inferred from column names, such as customer_id to customers (high or medium confidence)Every session on the connectionsource: inferred, with the confidence
Relationships in an ontology a user loaded with load_my_ontologyThat user's session onlysource: ontology

A declared key always outranks the other two between the same pair of tables. Lakehouse layers often declare no keys at all, and join paths still work there; Join Discovery Without Declared Keys covers the three ways to give OBA the joins.

On that graph, OBA computes:

Large schemas are also clustered into communities of related tables, with central tables and suggested domain names, so a model or a person can get a map of an unfamiliar layer before asking anything.

One call per question

graphrag_query_context runs the whole flow in a single tool call:

  1. Turn the question into a vector, once.
  2. Find the nearest tables and columns, and the business names that match.
  3. Add the directly neighbouring tables.
  4. Compute the join paths between the hits from the graph.
  5. Flag fan-trap risks along those paths.
  6. Return only that, with a token estimate.
question
  -> vector search: the relevant tables and columns
  -> graph search:  joins and risks
  -> compact context for the model

The model writes its SQL from this context instead of from the full schema, and the joins it uses are grounded in keys rather than guessed from names. Database views are indexed too and are found by graphrag_search, which is often the best answer to a business question, since a view is SQL an analyst already wrote and checked.

Related OrionBelt® Concepts

Frequently Asked Questions

What does GraphRAG mean in OrionBelt® Analytics?

Retrieval that uses two searches. Vector search finds the tables and columns whose meaning is close to the question. Graph search walks the keys between them to find how they join. The model receives only that subset of the schema, with its join paths and fan-trap risks.

Does schema data leave the server for embedding?

No. The embedding model, all-MiniLM-L6-v2 by default or a multilingual model if you choose it, runs inside the OBA server on the CPU through the ONNX runtime. The Docker image ships with both, so no internet access, external API or API key is needed.

Why do business names matter for vector search?

The model knows language, not your abbreviations. A bare net_amt scores lower against "revenue" than an unrelated ship_cost; with the business name "Net revenue" it becomes the clear best match. suggest_semantic_names and apply_semantic_names add those names, and every retrieval ranks them together with the schema's own names.

Can users ask in German when the schema is in English?

Yes, with GRAPHRAG_EMBEDDING_MODEL=multilingual. The default model is English-only: "Umsatz" scores 0.06 against "Net revenue". The multilingual model scores it 0.40 and ranks it first.

Does it work when the database declares no foreign keys?

Yes. The graph also holds relationships inferred from column names, marked as inferred with a confidence, and, for one user's session, relationships from an ontology that user loaded. A declared key always wins. See Join Discovery Without Declared Keys.

What happens when two join paths are equally short?

graphrag_find_join_path reports the path as ambiguous and lists the alternatives with their joins, so the model asks or chooses deliberately instead of silently.

Can it run in an air-gapped network?

Yes. The Docker image already contains both embedding models. A keyword-only TF-IDF backend is also available, but it matches words rather than meaning, so questions phrased in business terms often find nothing useful.

The right tables, joined by real keys

GraphRAG ships with OrionBelt® Analytics, the ontology-based MCP server for Text-to-SQL across 8 database connectors.

OrionBelt® Analytics Docs GitHub Contact RALFORION