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).
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 |
|
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 encpp/CMakeLists.txt. - Nuevo paquete
cpp/functions/duck/:duck_open(path) -> duckdb_database(conduckdb_open+duckdb_connect).duck_query(conn, sql, params) -> resultwrapper.
entity_ops(otableview.{h,cpp}en la app) — funciones a nivel de app:tableview_create(ops_db, duckdb_path, duck_table, row_type, char* out_id)crea entidadTablecon metadata + commit enoperations.db.tableview_count(duckdb_path, sql_filter, int64_t* out).tableview_page(duckdb_path, sql_filter, offset, limit, vector<TablePageRow>* out).TablePageRowlleva los campos delcolumns[]resueltos a string +promoted(LEFT JOIN contraops.entities).
graph_load_from_operations: filtrar relacionesCONTAINS_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".
- Detectar nodos
Definicion de hecho
- DuckDB compila y linka en linux + windows (cmake target).
- Smoke test: abrir un
.duckdbvacio, crear tabla con 1M filas (CTAS desderange), correrSELECT COUNT(*)< 100 ms. tableview_create+tableview_count+tableview_pagecon tests.- Un nodo
type_ref='Table'en el grafo se renderiza con un cuadrado overlay encima del circulo GPU, con contador de filas obtenido portableview_count. - El contador refresca al recargar el grafo o tras un INSERT en su DuckDB.