Structured Data Knowledge Graph: Your Tables and Your Documents in One Graph FalkorDB's GraphRAG-SDK now ingests CSV, TSV, PSV and tab files through a "record path" that writes each row as typed nodes and edges into the same FalkorDB graph as documents, with no LLM call per row. Tables are declared once in the ontology via TableMapping, Column and Link classes, so an employees.csv row becomes a Person node with an INTEGER age and a WORKS_AT edge to an Organization, and a finalize() step joins near-identical names. The SDK matters because it lets teams combine structured tables and prose in one knowledge graph for retrieval instead of keeping text beside a table. Your tables and your documents, one knowledge graph. FalkorDB GraphRAG-SDK turns your tables and documents into one structured data knowledge graph, writing every CSV row onto the same node a document about that entity would use. Declare once what a table's columns mean, and each row becomes typed nodes and edges, with no LLM call per row. Here is how it works, how to use it, and the questions it lets you ask. The whole architecture in 39 seconds. Two kinds of source, two paths in, one node in FalkorDB, one answer out.Cyan: written by a table · Amber: from prose · Violet: the SDK A table already knows what its columns mean Real knowledge is half prose and half table. The board report is a PDF; the org chart is an HR export. The incident write-up is Markdown; the asset inventory is a CSV. A table carries two things a document doesn't: a stable key for every row and a known type for every column . GraphRAG-SDK keeps both, and that is what makes a structured data knowledge graph more than text beside a table. Tables take their own path into the same FalkorDB graph, the record path , which reads each row exactly as declared and builds the same node and edge shapes retrieval already understands. These are the two tables the examples build on: employees.csv | employee id | full name | age | job title | org id | |---|---|---|---|---| | E-1 | Alice Smith | 34 | Engineer | ORG-42 | | E-2 | Bob Jones | 45 | Analyst | ORG-42 | | E-3 | Carol White | 29 | Designer | ORG-42 | orgs.csv | org id | org name | country | |---|---|---| | ORG-42 | Acme Corp | Norway | | ORG-7 | Globex | United Kingdom | What goes in. Each employees.csv row becomes a Person with an INTEGER age you can average, and its org id becomes a WORKS AT edge to that Organization . A .csv , .tsv , .psv or .tab file takes the record path automatically. Declare it once, in the ontology A table of entities is declared with three classes: TableMapping , Column and Link . A mapping binds one file to one label, names the column that identifies a row and the column holding its name, types the other columns, and turns any column holding another entity's key into an edge. It lives in Ontology.tables , next to the entity types, because a mapping is schema. python import asyncio from graphrag sdk import Column, ConnectionConfig, Entity, GraphRAG, Link, LiteLLM, LiteLLMEmbedder, Ontology, TableMapping ONTOLOGY = Ontology entities= Entity label="Person" , Entity label="Organization" , tables= TableMapping source="employees.csv", matched on the file name label="Person", name="full name", what a document would call her key="employee id", what identifies the row properties={"age": Column "age", "INTEGER" , "title": "job title"}, a bare string is a STRING column links= Link "WORKS AT", to="Organization", by="org id" , , TableMapping source="orgs.csv", label="Organization", name="org name", key="org id", properties={"country": "country"}, , , async def main : async with GraphRAG connection=ConnectionConfig host="localhost", graph name="acme" , llm=LiteLLM model="openai/gpt-5.5" , embedder=LiteLLMEmbedder model="openai/text-embedding-3-large", dimensions=256 , embedding dimension=256, ontology=ONTOLOGY, enable cypher=True, as rag: await rag.ingest "employees.csv" rows → people + WORKS AT edges await rag.ingest "orgs.csv" fills in the organizations they point at await rag.ingest "acme report.pdf" a document, exactly as before await rag.finalize joins near-identical names, reports the rest asyncio.run main There is no mapping= argument. A table takes the record path on its extension and its declaration is found by file name, so ingesting a table looks exactly like ingesting a PDF. finalize runs once after loading: it builds the embeddings and indexes, joins names that match without being identical, and reports anything worth a look. | Field | What it does | |---|---| | source | The file this mapping describes, matched on its name. That name also prefixes every property the table writes, except the three join fields §03 . | | label | The label each row becomes. | | name | The column holding the row's name. The node id is built from it, which is what joins a row to a document that mentions the same name. | | key | The column that identifies a row, such as an employee id. Links and re-syncs find rows through it, and it identifies the row when no name is given. | | properties | {name: Column col, type } , or a bare string for STRING . Types: STRING , INTEGER , FLOAT , BOOLEAN , DATE , LIST . | | links | Columns that point at other entities. Each becomes an edge, creating a placeholder node if the target hasn't been loaded yet §04 . | Checked before anything is written The mapping is checked against the file's real header, and every cell against its declared type, before a single write. A mapping that doesn't fit raises MappingError with the reason, and the graph stays exactly as it was. FLOAT refuses nan and inf , so one bad cell can never turn an average into NaN . Numbers are read the way the exporting system meant them: 1,234.56 and the German 1.234,56 are both 1234.56. Pass strict mapping=True to insist that every column in the file is mapped. A row and a sentence land on one node This is the design the whole feature turns on. A row doesn't get a separate record node parked beside the entity. It writes directly onto the same entity node that documents create, so a fact from the table and a fact from the text are one hop apart: the same node. The bridge is the name . A row's node id is compute entity id name, label , the exact function document extraction already uses. When employees.csv and acme report.pdf both name Alice Smith as a Person , both compute alice smith person and write to the same node from the very first write. Every column carries the name of the table that wrote it Two sources writing one node need something to keep them from overwriting each other. GraphRAG-SDK encodes ownership in the property name: every property a table writes is stored as