Computed Fields
@Field({ computed }) declares a value the database produces, not one the caller writes. It never
takes part in an insert or an update, and reads like any other field.
Reach for one when the value is an expression over the row that no clause covers - arithmetic, a
string built from two columns, a date part. Counting a relation is not one of those: use
$count, which needs no field on the entity and works on
MongoDB too.
import { col, Entity, Id, Field, raw } from 'uql-orm';
@Entity()export class Product { @Id({ type: Number }) id?: number;
@Field({ type: Number }) cost?: number;
@Field({ type: Number }) salePrice?: number;
@Field({ type: Number, // `col` qualifies and escapes each column, which is what keeps the expression // correct once the query joins something. computed: raw`${col('salePrice')} - ${col('cost')}`, }) profit?: number;}stored: the one dial
Section titled “stored: the one dial”By default nothing is persisted: the expression is spliced into every statement that reads the field.
Add stored: true and it becomes a real column the engine keeps up to date, so it can be indexed,
constrained and read without recomputing.
@Field({ type: Number, computed: raw`"qty" * "price"`, stored: true })total?: number;Queries do not change. $select, $where and $sort read the field the same way either side of the
dial, so stored is something you flip after profiling without touching a call site.
| unstored (default) | stored: true |
|
|---|---|---|
| where the value is | recomputed per statement | a column, written by the engine |
| DDL | none | GENERATED ALWAYS AS (...) STORED |
| indexable | no | yes, like any column |
| expression | anything the dialect parses | must be deterministic, over the row |
| MongoDB | yes | no - SQL engines only |
A stored column needs a type, since a migration has to spell one out. Support is Postgres 12+,
MySQL 5.7+, MariaDB 5.2+, SQLite 3.31+.
Querying
Section titled “Querying”Computed fields behave exactly like regular fields. You can select them or filter by them.
Selection
Section titled “Selection”const products = await pool.findMany(Product, { $select: { id: true, profit: true },});SELECT "id", "salePrice" - "cost" "profit" FROM "Product"Filtering
Section titled “Filtering”const products = await pool.findMany(Product, { $select: { id: true }, $where: { profit: { $gte: 10 }, },});SELECT "id" FROM "Product" WHERE "salePrice" - "cost" >= $1