Deep Relations
Querying relations
Section titled “Querying relations”UQL’s query syntax is context-aware. When you query a relation, the available fields and operators are automatically suggested and validated based on that related entity.
Basic Population
Section titled “Basic Population”You can load a relation and select its specific fields using $populate.
import { User } from './shared/models/index.js';
const users = await querier.findMany(User, { $select: { id: true, name: true }, $populate: { profile: { $select: { picture: true } } // Load specific fields from a 1-1 relation }, $where: { email: { $iincludes: '@example.com' } }});-- Main query with LEFT JOIN for OneToOne relationSELECT "User"."id", "User"."name", "profile"."picture" "profile.picture" -- Prefixed alias for unflatteningFROM "User"LEFT JOIN "Profile" "profile" ON "profile"."userId" = "User"."id" AND "profile"."deletedAt" IS NULLWHERE "User"."email" ILIKE '%@example.com%'Advanced: Deep Selection & Mandatory Relations
Section titled “Advanced: Deep Selection & Mandatory Relations”Use $required: true inside a $populate block to enforce an INNER JOIN (by default UQL uses LEFT JOIN).
import { User } from './shared/models/index.js';
const latestUsersWithProfiles = await querier.findOne(User, { $select: { id: true, name: true }, $populate: { profile: { $select: { picture: true, bio: true }, $where: { bio: { $ne: null } }, $required: true // Enforce INNER JOIN } }, $sort: { createdAt: 'desc' },});-- INNER JOIN enforced by $required: trueSELECT "User"."id", "User"."name", "profile"."picture" "profile.picture", "profile"."bio" "profile.bio"FROM "User"INNER JOIN "Profile" "profile" ON "profile"."userId" = "User"."id" AND "profile"."bio" IS NOT NULL AND "profile"."deletedAt" IS NULLORDER BY "User"."createdAt" DESCLIMIT 1Filtering on Related Collections
Section titled “Filtering on Related Collections”You can filter and sort when populating collections (One-to-Many or Many-to-Many).
import { User } from './shared/models/index.js';
const authorsWithPopularPosts = await querier.findMany(User, { $select: { id: true, name: true }, $populate: { posts: { $select: { title: true, createdAt: true }, $where: { title: { $iincludes: 'typescript' } }, $sort: { createdAt: 'desc' }, $limit: 5 } }, $where: { name: { $istartsWith: 'a' } }});-- Main query (parent rows)SELECT "User"."id", "User"."name" FROM "User"WHERE "User"."name" ILIKE $1-- OneToMany relation loaded via a second querySELECT "Post"."title", "Post"."createdAt", "Post"."authorId"FROM "Post"WHERE "Post"."authorId" IN ($1, $2, ...) AND "Post"."title" ILIKE '%typescript%'ORDER BY "Post"."createdAt" DESCLIMIT 5Sorting by Related Fields
Section titled “Sorting by Related Fields”UQL allows sorting by fields of related entities directly in the $sort object.
const items = await querier.findMany(Item, { $select: { id: true, name: true }, $sort: { tax: { name: 1 }, measureUnit: { name: 1 }, createdAt: 'desc' }});SELECT "Item"."id", "Item"."name"FROM "Item"LEFT JOIN "Tax" "tax" ON "tax"."id" = "Item"."taxId" AND "tax"."deletedAt" IS NULLLEFT JOIN "MeasureUnit" "measureUnit" ON "measureUnit"."id" = "Item"."measureUnitId" AND "measureUnit"."deletedAt" IS NULLORDER BY "tax"."name", "measureUnit"."name", "Item"."createdAt" DESC
Relation Filtering (EXISTS Subqueries)
Section titled “Relation Filtering (EXISTS Subqueries)”Filter parent entities based on conditions on their ManyToMany or OneToMany relations. UQL generates efficient EXISTS subqueries automatically.
ManyToMany
Section titled “ManyToMany”// Find all posts that have a tag named 'typescript'const posts = await querier.findMany(Post, { $where: { tags: { name: 'typescript' } },});SELECT * FROM "Post"WHERE EXISTS ( SELECT 1 FROM "PostTag" WHERE "PostTag"."postId" = "Post"."id" AND "PostTag"."tagId" IN ( SELECT "Tag"."id" FROM "Tag" WHERE "Tag"."name" = $1 AND "Tag"."deletedAt" IS NULL )) AND "Post"."deletedAt" IS NULLOneToMany
Section titled “OneToMany”// Find users who have authored posts with 'typescript' in the titleconst users = await querier.findMany(User, { $where: { posts: { title: { $iincludes: 'typescript' } } },});SELECT * FROM "User"WHERE EXISTS ( SELECT 1 FROM "Post" WHERE "Post"."authorId" = "User"."id" AND "Post"."title" ILIKE '%typescript%' AND "Post"."deletedAt" IS NULL) AND "User"."deletedAt" IS NULLRelation Count Filtering ($size Subqueries)
Section titled “Relation Count Filtering ($size Subqueries)”Filter parent entities by the number of related records using $size. UQL generates efficient COUNT(*) subqueries. Accepts a number for exact match or any comparison operator ($eq, $ne, $gt, $gte, $lt, $lte, $between).
OneToMany
Section titled “OneToMany”// Find categories with at least 2 measure unitsconst categories = await querier.findMany(MeasureUnitCategory, { $where: { measureUnits: { $size: { $gte: 2 } } },});SELECT * FROM "MeasureUnitCategory"WHERE (SELECT COUNT(*) FROM "MeasureUnit" WHERE "MeasureUnit"."categoryId" = "MeasureUnitCategory"."id") >= $1 AND "deletedAt" IS NULLManyToMany
Section titled “ManyToMany”// Find items with more than 5 tagsconst items = await querier.findMany(Item, { $where: { tags: { $size: { $gt: 5 } } },});SELECT * FROM "Item"WHERE (SELECT COUNT(*) FROM "ItemTag" WHERE "ItemTag"."itemId" = "Item"."id") > $1Multiple Comparison Operators
Section titled “Multiple Comparison Operators”// Find items with between 2 and 10 tagsconst items = await querier.findMany(Item, { $where: { tags: { $size: { $between: [2, 10] } } },});