Skip to content

Virtual Fields

The virtual property of the @Field decorator allows you to define non-persistent fields whose values are calculated at runtime using SQL or MongoDB expressions.

UQL’s virtual fields use the QueryContext pattern, so the generated SQL stays correct and cheap to produce.

import { Entity, Id, Field, ManyToMany, raw } from 'uql-orm';
import { v7 as uuidv7 } from 'uuid';
@Entity()
export class Item {
@Id({ type: 'uuid', onInsert: () => uuidv7() })
id?: string;
@Field()
name?: string;
@ManyToMany({ entity: () => Tag, through: () => ItemTag, cascade: true })
tags?: Tag[];
@Field({
/**
* Define the value for a non-persistent field using a sub-query.
*/
virtual: raw(({ ctx, dialect, escapedPrefix }) => {
ctx.append('(');
dialect.count(ctx, ItemTag, {
$where: {
itemId: raw(({ ctx }) => ctx.append(`${escapedPrefix}.id`))
}
}, { autoPrefix: true });
ctx.append(')');
})
})
tagsCount?: number;
}

 

Virtual fields behave exactly like regular fields in your queries. You can select them or filter by them.

You write
const items = await querier.findMany(Item, {
$select: { id: true, tagsCount: true }
});
Generated SQL
SELECT
"id",
(SELECT COUNT(*) "count" FROM "ItemTag" WHERE "ItemTag"."itemId" = "id") "tagsCount"
FROM "Item"
You write
const items = await querier.findMany(Item, {
$select: { id: true },
$where: {
tagsCount: { $gte: 10 },
},
});
Generated SQL
SELECT "id" FROM "Item"
WHERE (SELECT COUNT(*) "count" FROM "ItemTag" WHERE "ItemTag"."itemId" = "id") >= $1