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
staticorm():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()
staticbuildIdSearchQuery<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()
staticbuildIdSearchQueryMongo(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()
staticbuildIdSearchQuerySQL(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()
staticbuildQueryFromNode<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
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()
staticbuildSearchQuery<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 = valuegt- Returns matches whose parameter is greater than the given value. e.g.param > valuegte- Returns matches whose parameter is greater than or equal to the given value. e.g.param >= valuein- 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 < valuelte- Returns matches whose parameter is less than or equal to than the given value. e.g.param < valuenot/ne- Returns matches whose parameter is not equal to the given value. e.g.param != valuerange- 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 nestedop(...)text included. Soeq(Support(EU))matchesSupport(EU),eq( a,b )matchesa,bandeq(ne(x))matchesne(x). The operator name is case-insensitive. - A bare value shaped like
name(...)whosenameis not a known operator is rejected with a 400; wrap it ineq(...)to match it literally. eq()/ne()operands are then coerced like any operand:meresolves to the requesting user's uid,nullmatches 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()andrange()split their operand on commas. Write\,for a comma inside one value and\\for a backslash, e.g.in(a\,b,c)matchesa,borc. A backslash before any other character is kept as is.$orand$and(only from programmatic queries or theqJSON 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()
staticbuildSearchQueryMongo(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()
staticbuildSearchQuerySQL(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()
staticgetIdPropertyNames(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()
staticgetReadOnlyPropertyNames(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()
staticisUnsafeRegexPattern(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()
staticliteral(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
loadModels()
staticloadModels(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()
staticresolvePagination(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()
staticsetTypeOrm(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()
statictoFindQuery(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()
statictoTsQuery(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
Returns
string