Skip to main content

Class: ModelUtils

Defined in: service-core/src/models/ModelUtils.ts:120

Utility class for working with data model classes.

Constructors​

Constructor​

new ModelUtils(): ModelUtils

Returns​

ModelUtils

Accessors​

orm​

Get Signature​

get static orm(): any

Defined in: service-core/src/models/ModelUtils.ts:192

Returns the typeorm module, throwing an error if it has not been provided.

Returns​

any

Methods​

buildIdSearchQuery()​

static buildIdSearchQuery<T>(repo, modelClass, id, version?, includeDeleted?): any

Defined in: service-core/src/models/ModelUtils.ts:403

Builds a query object for use with find functions of the given repository for retrieving objects matching the specified unique identifier.

Type Parameters​

T​

T extends object

Parameters​

repo​

Repository<T> | MongoRepository<T> | undefined

The repository to build the query for.

modelClass​

any

The class definition of the data model to build a search query for.

id​

any

The unique identifier to search for.

version?​

number

The version number of the document to search for.

includeDeleted?​

boolean = true

Set to false to exclude soft-deleted RecoverableBaseEntity records from matching. Defaults to true (matches regardless of deleted state) to preserve existing lookup/validation behavior; callers that expose a record by id to an API client (e.g. findOne, exists) should pass false so a soft-deleted record doesn't remain fully readable/resurrectable, the same way buildSearchQuery already excludes deleted records by default for list-style queries.

Returns​

any

An object that can be passed to a TypeORM find function.


buildIdSearchQueryMongo()​

static buildIdSearchQueryMongo(modelClass, id, version?, includeDeleted?): any

Defined in: service-core/src/models/ModelUtils.ts:464

Builds a MongoDB compatible query object for use in find functions for retrieving objects matching the specified unique identifier.

Parameters​

modelClass​

any

The class definition of the data model to build a search query for.

id​

any

The unique identifier to search for.

version?​

number

The version number of the document to search for.

includeDeleted?​

boolean = true

Set to true to also match soft-deleted RecoverableBaseEntity records.

Returns​

any

An object that can be passed to a MongoDB find function.


buildIdSearchQuerySQL()​

static buildIdSearchQuerySQL(modelClass, id, version?, includeDeleted?): any

Defined in: service-core/src/models/ModelUtils.ts:426

Builds a TypeORM compatible query object for use in find functions for retrieving objects matching the specified unique identifier.

Parameters​

modelClass​

any

The class definition of the data model to build a search query for.

id​

any

The unique identifier to search for.

version?​

number

The version number of the document to search for.

includeDeleted?​

boolean = true

Returns​

any

An object that can be passed to a TypeORM find function.


buildQueryFromNode()​

static buildQueryFromNode<T>(modelClass, repo, node, user?): any

Defined in: service-core/src/models/ModelUtils.ts:1875

Compiles a QueryNode boolean tree into a query object for the given repository - the nested-condition counterpart to buildSearchQuery()'s flat op(value) query-parameter form, for boolean shapes the flat form can't express (e.g. (a AND b) OR (c AND d), with no key forced into every branch). Reuses the same operand coercion, me substitution and operator-injection guard as the flat form. Bounded by the same MAX_QUERY_DEPTH/MAX_QUERY_NODES limits as $or. Negated groups are supported on MongoDB (via $nor) but rejected against the SQL find()-based where (see compileNodeSQL).

Type Parameters​

T​

T extends object

Parameters​

modelClass​

any

The class definition of the data model to build a search query for.

repo​

Repository<T> | MongoRepository<T> | undefined

The repository to build a search query for.

node​

QueryNode

The root of the query tree.

user?​

any

The user that is performing the request, resolved for any field: "me" predicate value.

Returns​

any


buildSearchQuery()​

static buildSearchQuery<T>(modelClass, repo, query?, exactMatch?, user?): any

Defined in: service-core/src/models/ModelUtils.ts:1168

Builds a query object for the given criteria and repository. Query params can have a value containing a conditional operator to apply for the search. The operator is encoded with the format op(value). The following operators are supported:

  • eq - Returns matches whose parameter exactly matches of the given value. e.g. param = value
  • gt - Returns matches whose parameter is greater than the given value. e.g. param > value
  • gte - Returns matches whose parameter is greater than or equal to the given value. e.g. param >= value
  • in - Returns matches whose parameter includes one of the given values. e.g. param in ('value1', 'value2', 'value3', ...)
  • like - Returns matches whose parameter matches the given glob pattern (* = any sequence, ? = any single character), case-insensitively. e.g. like(*.txt)
  • regex - Returns matches whose parameter matches the given regular expression, case-insensitively.
  • lt - Returns matches whose parameter is less than the given value. e.g. param < value
  • lte - Returns matches whose parameter is less than or equal to than the given value. e.g. param < value
  • not / ne - Returns matches whose parameter is not equal to the given value. e.g. param != value
  • range - Returns matches whose parameter is greater than or equal to first given value and less than or equal to the second. e.g. param between(1,100)
  • exists - Returns matches whose parameter is (exists(true)) or is not (exists(false)) set.

When no operator is provided the comparison is evaluated as eq, unless exactMatch is false, in which case a string-valued parameter is instead matched as a case-insensitive "contains" search.

Operand and escaping rules:

  • A value is only parsed as an operator when the WHOLE value has the shape name(...). The operand is everything between the first ( and the last ), verbatim: parentheses, commas, leading/trailing spaces, newlines and nested op(...) text included. So eq(Support(EU)) matches Support(EU), eq( a,b ) matches a,b and eq(ne(x)) matches ne(x). The operator name is case-insensitive.
  • A bare value shaped like name(...) whose name is not a known operator is rejected with a 400; wrap it in eq(...) to match it literally.
  • eq()/ne() operands are then coerced like any operand: me resolves to the requesting user's uid, null matches a null value, and the value is converted to the column's declared type (number, boolean, date). With no column metadata a JSON/date heuristic is used instead.
  • in(), nin() and range() split their operand on commas. Write \, for a comma inside one value and \\ for a backslash, e.g. in(a\,b,c) matches a,b or c. A backslash before any other character is kept as is.
  • $or and $and (only from programmatic queries or the q JSON parameter; a query string can't build them) take a non-empty array of sub-query objects that are ANDed with every other key, the same on both backends. A $-prefixed field name is rejected with a 400.

Code that passes a value it doesn't control (an email header, an iCalendar UID, a display name, ...) should use ModelUtils.literal(value) instead of building an eq(...) string. A literal skips all of the parsing and coercion above. Other non-string values (numbers, booleans, Date, null) are also compared as given.

A repeated query parameter name (e.g. ?a=1&a=2) OR-combines its values, "zipped" positionally against every other repeated parameter rather than as a cartesian product: ?a=1&a=2&b=3&b=4 compiles to (a=1 AND b=3) OR (a=2 AND b=4), not a IN (1,2) and not all four combinations. A shorter array is padded by repeating its own last value against the longer one(s), rather than leaving the key unset for the extra branches (which would match ANY value there, silently dropping that filter).

NOTE: The result of this function is only compatible with the aggregate() function when MongoDB is used.

Type Parameters​

T​

T extends object

Parameters​

modelClass​

any

The class definition of the data model to build a search query for.

repo​

Repository<T> | MongoRepository<T> | undefined

The repository to build a search query for.

query?​

any = {}

The search query parameters to include.

exactMatch?​

boolean = false

Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.

user?​

any

The user that is performing the request.

Returns​

any

The TypeORM compatible query object.


buildSearchQueryMongo()​

static buildSearchQueryMongo(modelClass, query?, exactMatch?, user?, depth?): any

Defined in: service-core/src/models/ModelUtils.ts:1420

Builds a MongoDB compatible query object for the given criteria. See buildSearchQuery for the supported op(value) operators and multi-value "zip" semantics.

Unlike buildSearchQuery (which always injects a deleted: false filter for a RecoverableBaseEntity before delegating here), this function applies no soft-delete filtering of its own - a caller invoking it directly, bypassing buildSearchQuery, will not get that default exclusion.

Does NOT bound limit/page into the returned pipeline (see resolvePagination) and returns either an aggregation pipeline array or a flattened {$match, $sort} object depending on how many stages it produced (see toFindQuery to normalize to one shape).

NOTE: The result of this function is only compatible with the aggregate() function.

Parameters​

modelClass​

any

The class definition of the data model to build a search query for.

query?​

any = {}

The search query parameters to include.

exactMatch?​

boolean = false

Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.

user?​

any

The user that is performing the request.

depth?​

number = 0

Internal recursion-depth counter for nested $or groups - do not pass explicitly.

Returns​

any

The MongoDB compatible query object.


buildSearchQuerySQL()​

static buildSearchQuerySQL(modelClass, query?, exactMatch?, user?, driverType?, depth?): any

Defined in: service-core/src/models/ModelUtils.ts:1209

Builds a TypeORM compatible query object for the given criteria. See buildSearchQuery for the supported op(value) operators and multi-value "zip" semantics.

Unlike buildSearchQuery (which always injects a deleted: false filter for a RecoverableBaseEntity before delegating here), this function applies no soft-delete filtering of its own - a caller invoking it directly, bypassing buildSearchQuery, will not get that default exclusion.

Parameters​

modelClass​

any

The class definition of the data model to build a search query for.

query?​

any = {}

The search query parameters to include.

exactMatch?​

boolean = false

Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.

user?​

any

The user that is performing the request.

driverType?​

string

The TypeORM driver type (connection.options.type) of the target datasource, used to select a compatible SQL translation for the regex() operator. Only needed when regex() may appear in query.

depth?​

number = 0

Internal recursion-depth counter for nested $or groups - do not pass explicitly.

Returns​

any

The TypeORM compatible query object.


getIdPropertyNames()​

static getIdPropertyNames(modelClass): string[]

Defined in: service-core/src/models/ModelUtils.ts:207

Retrieves a list of all of the specified class's properties that have the

Parameters​

modelClass​

any

The class definition to search for identifiers from.

Returns​

string[]

The list of all property names that have the

Identifier​

decorator applied.

Identifier​

decorator applied.


getReadOnlyPropertyNames()​

static getReadOnlyPropertyNames(modelClass): string[]

Defined in: service-core/src/models/ModelUtils.ts:241

Retrieves a list of all of the specified class's properties that have the

Parameters​

modelClass​

any

The class definition to search for read-only properties from.

Returns​

string[]

The list of all property names that have the

Read Only​

decorator applied.

Read Only​

decorator applied.


isUnsafeRegexPattern()​

static isUnsafeRegexPattern(pattern): boolean

Defined in: service-core/src/models/ModelUtils.ts:674

Best-effort check for regex patterns vulnerable to catastrophic backtracking (ReDoS): patterns that are unreasonably long, that contain a quantified group whose contents are themselves quantified (e.g. (a+)+, (a*)*), or a quantified group containing alternation (e.g. (a|a)*, (a|ab)*) - both classic shapes that cause exponential backtracking in JS's (and SQLite's, since the regex() SQL operator is backed by a JS RegExp - see registerRegexpFunction in TypeOrmSupport.ts) regex engine. This is not an exhaustive defense; it catches the common cases a client would realistically send. like() no longer accepts raw regex (it compiles glob syntax instead - see globToRegExpSource), so this now guards only the explicit regex() operator.

Public so the SQLite REGEXP custom function (registered per-connection in TypeOrmSupport.ts) can apply the same guard at query-execution time, since a pattern reaching that function didn't necessarily pass through this class's own query builders (e.g. a raw Raw()/QueryBuilder use elsewhere).

Parameters​

pattern​

string

Returns​

boolean


literal()​

static literal(value, op?): QueryLiteral

Defined in: service-core/src/models/ModelUtils.ts:151

Marks value as a literal search value for buildSearchQuery() (and so RepoUtils.find/count/truncate), so it is compared exactly as given instead of being parsed as op(value) syntax. Use this whenever a query value comes from outside the code (a client, an email header, an iCalendar UID, a display name): a raw string such as ne(x) would otherwise be read as an operator, Support(EU) would be rejected as an unknown operator, me/null would be substituted, and a comma inside an in() list would split the value.

The value is not type-coerced, so pass it with the column's real type (number, boolean, Date, string). null still compiles to IS NULL on SQL. Object values are still checked for hidden $/dotted keys.

repoUtils.find({ messageId: ModelUtils.literal(header) });
repoUtils.find({ uid: ModelUtils.literal(["a,b", "c"], "in") });
repoUtils.find({ name: ModelUtils.literal(displayName, "ne") });
repoUtils.find({ size: ModelUtils.literal([10, 20], "range") });

Parameters​

value​

unknown

The value to compare against. An array for in, nin and range (exactly two elements).

op?​

LiteralOperator = "eq"

The comparison to apply. Defaults to eq.

Returns​

QueryLiteral


loadModels()​

static loadModels(src, result?): Promise<Map<string, any>>

Defined in: service-core/src/models/ModelUtils.ts:1920

Loads all model schema files from the specified path and returns a map containing all the definitions.

Parameters​

src​

string

The path to the model files to load.

result?​

Map<string, any> = ...

Returns​

Promise<Map<string, any>>

A map containing of all loaded model names to their class definitions.


resolvePagination()​

static resolvePagination(query?): object

Defined in: service-core/src/models/ModelUtils.ts:1034

Resolves the limit/page reserved query parameters to a bounded take/skip pair, applying the same default (DEFAULT_PAGE_SIZE) and ceiling (MAX_PAGE_SIZE) that buildSearchQuerySQL already bakes into its own return value (as take/page). buildSearchQueryMongo does NOT bake pagination into its own pipeline - doing so would execute as $skip/$limit aggregation stages, which would double up with (and corrupt) any cursor-level .skip()/.limit() a caller applies on top, as RepoUtils already does for its own route-level pagination. A caller building a Mongo query directly - rather than going through RepoUtils - should call this explicitly to get the same bounded pagination the SQL path enforces automatically, rather than an unbounded result set.

Parameters​

query?​

any = {}

Returns​

object

page​

page: number

skip​

skip: number

take​

take: number


setTypeOrm()​

static setTypeOrm(module): void

Defined in: service-core/src/models/ModelUtils.ts:185

Provides the typeorm module to use when building SQL queries. This is called automatically when a SQL datasource connection is established.

Parameters​

module​

any

The typeorm module.

Returns​

void


toFindQuery()​

static toFindQuery(pipelineOrObject): any[]

Defined in: service-core/src/models/ModelUtils.ts:1010

Normalizes the return value of buildSearchQueryMongo to a single shape: a full aggregation pipeline. buildSearchQueryMongo itself still returns either a pipeline array or a flattened {$match, $sort} object depending on how many stages it produced (existing callers, e.g. RepoUtils, already branch on Array.isArray() to handle both) - use this instead at any new call site that wants one consistent shape.

Parameters​

pipelineOrObject​

any

Returns​

any[]


toTsQuery()​

static toTsQuery(node): string

Defined in: service-core/src/models/ModelUtils.ts:1897

Converts a QueryNode boolean tree into a PostgreSQL tsquery expression string (AND -> &, OR -> |, negation -> !), so client input can drive full-text search without passing untrusted text straight to to_tsquery (which throws on malformed input) while still supporting the boolean grouping websearch_to_tsquery cannot express. Every predicate leaf's value is treated as a search term (lexeme/phrase) regardless of its field/op - this framework has no notion of a full-text-indexed column, so the caller is expected to route the resulting expression to whichever tsvector column it's searching, e.g. to_tsquery(ModelUtils.toTsQuery(node)).

Parameters​

node​

QueryNode

Returns​

string