Files
graph_explorer/issues/0010-table-node.md
T
egutierrez 20d8bbf360 docs(issues): rewrite 0010 con DuckDB + new 0011 (UI fase 2)
0010 cambia de modelo SQLite CONTAINS_ROW a tier DuckDB:
operations.db sigue con grafo + filas promovidas, tablas grandes
viven en projects/<proj>/apps/graph_explorer/tables/<slug>.duckdb.
0011 separa la fase 2 (UI expandida + promote/demote + ingesta CSV).
2026-05-01 01:14:52 +02:00

3.3 KiB

id, title, status, priority, created, revised, depends_on
id title status priority created revised depends_on
0010 Nodo tabla — DuckDB foundation + render colapsado pending high 2026-04-30 2026-05-01
0004
0005
0008

Objetivo

Tier de almacenamiento tabular para nodos Table que pueden contener millones de filas sin saturar operations.db ni el grafo. Las "filas" viven en un .duckdb por proyecto (o por tabla) y se promueven a entidades reales del grafo solo cuando se necesita interactuar con ellas individualmente (relaciones, edicion, etc.).

Esta issue cubre fase 1 — vendoring de DuckDB, funciones tableview_* core, y render colapsado del nodo Table en el viewport. La fase 2 (UI expandida, paginacion, promote/demote, ingesta CSV/Parquet) va en issue 0011.

Modelo de datos

Dos tiers por proyecto:

projects/<proj>/apps/graph_explorer/
  operations.db                # SQLite — grafo (entities + relations + filas promovidas)
  tables/
    <slug>.duckdb              # DuckDB — bulk tabular (millones de filas)

El nodo Table es una entidad normal con type_ref = 'Table' y metadata que apunta a su dataset DuckDB. No contiene filas internamente — es una vista.

{
  "duckdb_path":  "tables/sospechosos.duckdb",
  "table_name":   "people",
  "row_type":     "Person",
  "id_column":    "id",
  "label_column": "name",
  "columns":      ["name","age","email"],
  "filter_sql":   "",
  "expanded":     false
}

Una fila promovida es una entidad en operations.db con metadata de origen:

{
  "source": {
    "duckdb": "tables/sospechosos.duckdb",
    "table":  "people",
    "row_id": "p_42"
  },
  "name": "Ana Lopez",
  "age":  33
}

Cambios en codigo

  • Vendor DuckDB en cpp/vendor/duckdb/ (amalgamation o precompiled). Add library en cpp/CMakeLists.txt.
  • Nuevo paquete cpp/functions/duck/:
    • duck_open(path) -> duckdb_database (con duckdb_open + duckdb_connect).
    • duck_query(conn, sql, params) -> result wrapper.
  • entity_ops (o tableview.{h,cpp} en la app) — funciones a nivel de app:
    • tableview_create(ops_db, duckdb_path, duck_table, row_type, char* out_id) crea entidad Table con metadata + commit en operations.db.
    • tableview_count(duckdb_path, sql_filter, int64_t* out).
    • tableview_page(duckdb_path, sql_filter, offset, limit, vector<TablePageRow>* out).
    • TablePageRow lleva los campos del columns[] resueltos a string + promoted (LEFT JOIN contra ops.entities).
  • graph_load_from_operations: filtrar relaciones CONTAINS_ROW (heredado del modelo viejo, ya no se emiten pero por si se topa con dbs antiguas).
  • views.cpp:
    • Detectar nodos type_ref == "Table" al renderizar etiquetas/contadores.
    • Overlay con ImGui::GetForegroundDrawList() por cada nodo Table: rectangulo redondeado + label "Table · N filas".

Definicion de hecho

  • DuckDB compila y linka en linux + windows (cmake target).
  • Smoke test: abrir un .duckdb vacio, crear tabla con 1M filas (CTAS desde range), correr SELECT COUNT(*) < 100 ms.
  • tableview_create + tableview_count + tableview_page con tests.
  • Un nodo type_ref='Table' en el grafo se renderiza con un cuadrado overlay encima del circulo GPU, con contador de filas obtenido por tableview_count.
  • El contador refresca al recargar el grafo o tras un INSERT en su DuckDB.