asegurate de que subimos todo
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
---
|
||||
id: 0078
|
||||
title: tables playground — joins MBQL-style (fase 9)
|
||||
status: pending
|
||||
priority: medium
|
||||
created: 2026-05-12
|
||||
related_components: [cpp/apps/primitives_gallery/playground/tables, lua_engine, tql]
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
Fase 9 del roadmap del tables playground. Hoy `render()` acepta un solo input
|
||||
table. MBQL permite `:joins` para combinar varias tablas en una sola query.
|
||||
Queremos lo mismo en TQL.
|
||||
|
||||
Roadmap restante tras esta fase:
|
||||
- 10: drill-through extendido
|
||||
- 11: LLM API ("Ask AI")
|
||||
- 12: promote a registry + migrar apps C++
|
||||
|
||||
## Diseño (referencia MBQL)
|
||||
|
||||
MBQL `:joins`:
|
||||
|
||||
```clojure
|
||||
:joins [{:source-table 2
|
||||
:alias "orders"
|
||||
:condition [:= [:field "user_id"] [:field "user_id" {:join-alias "orders"}]]
|
||||
:strategy :left-join
|
||||
:fields :all}]
|
||||
```
|
||||
|
||||
Adaptacion a nuestro modelo:
|
||||
- Sin DB ids — usamos nombres de inputs pasados en runtime.
|
||||
- Soportar multi-key composite via vector de pares.
|
||||
- Strategies: `left` | `inner` | `right` | `full`.
|
||||
- Fields: `all` | `none` | lista.
|
||||
|
||||
## Cambios
|
||||
|
||||
### Tipos / API
|
||||
|
||||
- `struct TableInput { string name; vector<string> headers; vector<ColumnType> types; vector<const char*> cells; int rows; int cols; }`
|
||||
- `struct Join { string alias; string source; vector<pair<string,string>> on; JoinStrategy strategy; vector<string> fields; }` en `data_table_logic.h`.
|
||||
- `State.joins: vector<Join>` (antes de stages[0]).
|
||||
- `render()` signature extendida:
|
||||
```cpp
|
||||
void render(const char* id,
|
||||
const char* const* headers, int col_count,
|
||||
const char* const* cells, int row_count,
|
||||
State& st,
|
||||
const ColumnType* declared_types,
|
||||
const std::vector<TableInput>* joinables = nullptr);
|
||||
```
|
||||
|
||||
### Logica pura
|
||||
|
||||
- `join_tables_cpp_core(left, right, alias, on, strategy, fields) -> StageOutput`.
|
||||
- Tests: left/inner/right/full join + multi-key + NULL handling (left propaga `""`).
|
||||
- Pre-pipeline: si `state.joins` no vacio, materializar tabla joined (recorriendo joinables[]) ANTES de aplicar stage 0.
|
||||
- Headers post-join prefijados: `alias.col` (preserva originales del main).
|
||||
|
||||
### UI
|
||||
|
||||
- **Chip row "Joins:"** debajo de "Filters:". + button abre popup con:
|
||||
- Combo alias (auto-sugerido) + combo source (nombre de input)
|
||||
- Strategy combo
|
||||
- on[] list: par left-col / right-col con + para añadir mas pares (multi-key)
|
||||
- Fields radio: all / none / pick
|
||||
- Chip muestra `alias <- source on left=right (left-join)`. Right-click edit, X borrar.
|
||||
- **CONDICIONAL**: la fila de joins solo se renderiza si `joinables != nullptr && !joinables->empty()`. Sin tablas extra → no UI de joins, no se pierde espacio en apps que solo pasan una tabla.
|
||||
|
||||
### TQL
|
||||
|
||||
Nuevo bloque root-level:
|
||||
|
||||
```lua
|
||||
return {
|
||||
version = 1,
|
||||
display = "table",
|
||||
joins = {
|
||||
{alias = "orders", source = "orders_tbl",
|
||||
on = { {"user_id", "user_id"} },
|
||||
strategy = "left", fields = "all"},
|
||||
},
|
||||
stages = { ... },
|
||||
columns = { ... },
|
||||
views = { ... },
|
||||
}
|
||||
```
|
||||
|
||||
emit/apply round-trip + tests.
|
||||
|
||||
### Lua engine
|
||||
|
||||
YA aplicado preempt (2026-05-12): `ident_cont` acepta `.` para parsear
|
||||
`[alias.col]` post-join sin colision.
|
||||
|
||||
### compute_stage / find_orig_col
|
||||
|
||||
No requieren cambios — operan sobre strings de col names. Aceptan `alias.col` directamente.
|
||||
|
||||
### extra_panels
|
||||
|
||||
No requieren cambios — ven el StageOutput post-join automaticamente.
|
||||
|
||||
## Pasos de implementacion
|
||||
|
||||
1. `join_tables_cpp_core` (pure) + tests unit.
|
||||
2. `TableInput` struct + `Join` struct + `State.joins`.
|
||||
3. `render()` signature extendida (default `nullptr`, back-compat).
|
||||
4. Pre-pipeline materialize join cuando `state.joins` no vacio.
|
||||
5. UI chip row (condicional a joinables disponibles).
|
||||
6. TQL emit/apply joins + tests round-trip.
|
||||
7. Lua `[alias.col]` resolver test (la sintaxis ya parsea).
|
||||
8. Actualizar tests phase9: 4 join types + multi-key + NULL + TQL round-trip.
|
||||
|
||||
## No-objetivos (esta fase)
|
||||
|
||||
- Subqueries / source-query MBQL — no aplicable, las inputs ya estan materializadas.
|
||||
- Joins recursivos — flat list, sin chains internos.
|
||||
- Outer joins con conditions no-igualdad (`>`, `<`, range) — solo `=` por ahora.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Performance con tablas grandes — hash join sobre key cols. Para v1, nested loop
|
||||
con hash map sobre right table es suficiente.
|
||||
- Memoria — joined table puede ser N*M en worst case. Documentar.
|
||||
Reference in New Issue
Block a user