Skip to main content

Derived queries

The Data plugin can generate read queries from a repository method's name. It parses the method against the entity's TypeORM metadata, binds method arguments as query parameters, and dispatches to SQLQueryEngine or MongoQueryEngine according to the owning data source.

Method structure​

The supported shape is:

<operation-prefix><predicate>[OrderBy<property><direction>[And<property><direction>...]]

The property names come from the entity's TypeORM metadata. Use entity property names in camel case; the parser resolves them against metadata rather than passing arbitrary field names into SQL.

PrefixResultExample
findByPromise<Entity[]>findByStatus(status)
findOneByEntity or nullfindOneByEmail(email)
countByPromise<number>countByStatus(status)
existsByPromise<boolean>existsByEmail(email)

Use declare to add these method types to your repository class; the proxy provides the runtime implementation.

Predicates and operators​

Without a suffix, a property means equality. These suffixes are supported:

SuffixArgumentsMeaning
(none)one valueEquality
IsNullnoneProperty is null
IsNotNullnoneProperty is not null
GreaterThanone value>
GreaterThanEqualone value>=
LessThanone value<
LessThanEqualone value<=
Betweenlower and upper valuesInclusive range
Inone arrayValue is in the array
Likeone stringPattern matching

The number and order of arguments must match the predicates from left to right. A predicate with Between consumes two arguments; In consumes one array argument.

declare findByStatusAndAgeGreaterThan: (status: string, age: number) => Promise<User[]>;
declare findByCreatedAtBetween: (from: Date, to: Date) => Promise<User[]>;
declare findByStatusIn: (statuses: string[]) => Promise<User[]>;

And binds more tightly than Or. For example:

declare findByStatusAndAgeGreaterThanOrEmail: (status: string, age: number, email: string) => Promise<User[]>;

means (status = ? AND age > ?) OR (email = ?). Empty groups and unknown property/operator names are rejected with an error.

Ordering​

Add one OrderBy clause after all predicates. Separate multiple properties with And, and add Asc or Desc to each property. The direction defaults to ascending:

declare findByStatusOrderByAgeDescAndEmailAsc: (status: string) => Promise<User[]>;
declare findByStatusOrderByEmail: (status: string) => Promise<User[]>; // email ASC

countBy and existsBy do not accept OrderBy, because ordering cannot affect those results.

Pagination​

Pass a PageRequest as the final argument of findBy methods. page is zero-based, and size must be a positive integer:

import { PageRequest } from '@axisparkjs/data';

declare findByStatusOrderByCreatedAtDesc: (status: string, page?: PageRequest) => Promise<User[]>;

const firstPage = await users.findByStatusOrderByCreatedAtDesc('active', { page: 0, size: 25 });

The SQL engine applies TypeORM skip and take; the Mongo engine applies skip and take to Mongo find options. Pagination is supported only for findBy. SQL Server (mssql) requires an OrderBy when paging, so include one in the method name.

SQL and MongoDB mapping​

The same method format is translated to the active database's query model:

  • SQL predicates use TypeORM SelectQueryBuilder, SQL comparison operators, and bound parameters. Like uses SQL % and _ wildcards.
  • MongoDB predicates become Mongo filters such as $gt, $gte, $lt, $lte, $in, $and, and $or. Like converts % to .* and _ to ., and uses a case-sensitive regular expression.
  • IsNull maps to Mongo's { field: null }, which also matches documents where the field is absent. IsNotNull maps to { field: { $ne: null } }.

SQL Like case sensitivity depends on the database collation. Mongo's generated regex is case-sensitive. Derived methods currently support reads only; use the ordinary save and remove methods, or TypeORM directly, for writes and deletes.