Consultas¶
Com o modelo User da página anterior, vamos montar consultas SELECT. No
tempest-db-js, select(...) devolve um builder encadeável que carrega o tipo do
resultado — antes mesmo de tocar num banco.
Montar é separado de executar
select(...) monta uma AST tipada — não toca no banco sozinho. Quem roda é
o session.execute(...), que você vê em Executando queries.
Separar as duas coisas deixa toda a segurança de tipos testável só com o
compilador, e o select reaproveitável em qualquer sessão.
Passo 1 — Selecionar tudo¶
O tipo de resultado de q é UserRow[] — todas as colunas, inferidas da classe.
Sem anotação manual.
Passo 2 — Filtrar com where¶
As chaves de where são checadas contra as colunas de User. Errar o nome é
erro de compilação:
Operadores tipados por coluna¶
O valor de cada filtro aceita um match exato (shorthand de eq) ou um objeto de
operador. E o conjunto de operadores é restrito ao tipo da coluna — usar um
operador inválido é erro de compilação:
| Tipo da coluna | Operadores |
|---|---|
string (varchar/text/uuid/enum) |
eq, ne, in, notIn, like, ilike, isNull |
number / bigint / Date |
eq, ne, in, notIn, gt, gte, lt, lte, between, isNull |
boolean |
eq, ne, isNull |
| json / blob | eq, ne, in, notIn, isNull |
select(User).where({
age: { gt: 18, lte: 65 }, // ✅ ordenados em number
name: { like: "%Ben%" }, // ✅ like em string
active: true, // ✅ shorthand de eq
tags: { isNull: false }, // ✅ em qualquer coluna
});
// ❌ erro de compilação: `like` não existe em number
select(User).where({ age: { like: "%18%" } });
// ❌ erro de compilação: `gt` não existe em string
select(User).where({ name: { gt: "a" } });
Por que isso importa
O operador errado pro tipo da coluna é um bug que normalmente só aparece em runtime (ou nunca). Aqui ele não compila — o tipo da coluna carrega quais comparações fazem sentido.
Combinadores and / or / not¶
A forma objeto é um AND implícito. Pra OR, NOT ou aninhar lógica, use os
combinadores and/or/not — eles funcionam em select, update, delete e
join:
import { and, or, not } from "tempest-db-js";
// (age < 18) OR (age > 65)
select(User).where(or({ age: { lt: 18 } }, { age: { gt: 65 } }));
// active AND NOT (age < 18)
select(User).where(and({ active: true }, not({ age: { lt: 18 } })));
Key-safety nos combinadores
A forma objeto no topo (where({...})) já checa as chaves contra as colunas.
Dentro dos combinadores, passe o tipo da linha pra checagem total —
or<UserRow>({...}, {...}) — senão as chaves ficam permissivas.
Passo 3 — Ordenar, limitar, paginar¶
Os métodos encadeiam e são imutáveis (cada um devolve um novo builder):
const page = select(User)
.where({ age: { gte: 18 } })
.orderBy("age", "desc")
.limit(20)
.offset(40);
orderBy também valida a coluna:
Passo 4 — Projeção com Pick¶
Quer só algumas colunas? Passe a lista no segundo argumento de select. O tipo de
resultado vira um Pick exato:
const names = select(User, ["id", "name"]);
// resultado inferido: Pick<UserRow, "id" | "name">[]
// → { id: number; name: string }[]
A projeção sobrevive ao encadeamento — where, orderBy, limit não a
desfazem:
const q = select(User, ["id", "age"])
.where({ age: { gt: 18 } })
.orderBy("age", "desc");
// resultado: { id: number; age: number }[]
E projetar uma coluna inexistente é erro de compilação:
Inspecionando a AST¶
O builder expõe sua AST em .node — útil pra debug e pra entender o que será
compilado pra SQL pelo dialeto:
const q = select(User, ["id", "name"]).where({ age: { gt: 18 } }).limit(10);
console.log(q.node);
// {
// kind: "select",
// table: "users",
// columns: ["id", "name"],
// where: { age: { gt: 18 } },
// orderBy: [],
// limit: 10,
// offset: undefined,
// }
Recap¶
select(Model)→ builder com resultadoRow[].select(Model, [cols])→ resultado projetadoPick<Row, cols>[]..where({...})valida as chaves contra as colunas e os operadores por tipo..orderBy(col, dir),.limit(n),.offset(n)encadeiam e são imutáveis.- A AST fica em
.node; rodar é comsession.execute— próxima parte.
Agora vamos escrever dados. 👉 Inserir, atualizar, deletar