Server side repository. Override ServerRepository#select method return initialized TubDataStore
Usually is created by using one of the fabric functions:
- UB.Repository for entities from this server instance
- conn.Repository for access remote UB server
let store = UB.Repository('my_entity')
.attrs('id')
.where('code', 'in', ['1', '2', '3']) // code in ('1', '2', '3')
.where('name', 'contains', 'Homer') // name like '%homer%'
.where('birtday', 'geq', new Date()) //(birtday >= '2012-01-01')
.where('birtday', 'leq', new Date() + 10) // AND (birtday <= '2012-01-02')
.where('[age] -10', '>=', {age: 15}, 'byAge') // (age + 10 >= 15)
.where('', 'match', 'myvalue') // perform full text search for entity (require fts mixin)
.logic('(([byStrfType]) OR ([bySrfKindID]))AND(dasdsa)')
.select()
# new ServerRepository ()
Members
# UBQLv2 : boolean instance
Server side support UBQL v2 version (value in whereList)
# WhereCondition : string instance
Enumeration of all condition types. This enumeration defines a set of String values. It exists primarily for documentation purposes - in code use the actual string values like '>', don't reference them through this class like WhereCondition.more.
We define several aliases for the same condition. In case of direct HTTP request (without Repository) use only
non-aliased values (i.e. more
instead of '>' or 'gt')
Name | Type | Description |
---|---|---|
gt | string | Alias for more |
">" | string | Alias for more |
more | string | Greater than |
lt | string | Alias for less |
"<" | string | Alias for less |
less | string | Less than |
eq | string | Alias for equal |
"=" | string | Alias for equal |
equal | string | Equal to |
ge | string | Alias for moreEqual |
geq | string | Alias for moreEqual |
">=" | string | Alias for moreEqual |
moreEqual | string | Greater than or equal |
le | string | Alias for lessEqual |
leq | string | Alias for lessEqual |
"<=" | string | Alias for lessEqual |
lessEqual | string | Less than or equal |
ne | string | Alias for notEqual |
neq | string | Alias for notEqual |
"<>" | string | Alias for notEqual |
"!=" | string | Alias for notEqual |
"!==" | string | Alias for notEqual |
notEqual | string | Not equal |
contains | string | Alias for like |
like | string | Like condition. For attributes of type String only |
notContains | string | Alias for notLike |
notLike | string | Not like condition. For attributes of type String only |
isNull | string | Is null |
null | string | Alias for isNull |
notNull | string | Alias for notIsNull |
notIsNull | string | Not is null |
isNotNull | string | Alias for notIsNull |
isFalse | string | Is false, for attributes of type 'Boolean' only |
isTrue | string | Is true, for attributes of type 'Boolean' only |
beginWith | string | Alias for startWith |
startWith | string | Start with. For attributes of type String only |
startsWith | string | Alias for startWith |
startswith | string | Alias for startWith |
notBeginWith | string | Alias for notStartWith |
notStartWith | string | Not start with. For attributes of type String only |
notStartsWith | string | Alias for notStartWith |
includes | string | Alias for in |
in | string | One of. Can accept array of string on array of Int/Int64 as values depending on attribute type. |
notIncludes | string | Alias for notIn |
notIn | string | Not one of. See WhereCondition.in for details |
match | string | For entities with FTS mixin enabled. TODO - expand |
subquery | string | Execute a sub-query passed in values. Better to use 'in' condition with Repository as a values parameter or a CustomRepository.exists method |
exists | string | Execute a exists(sub-query) passed in values. Better to use CustomRepository.exists method |
notExists | string | Execute a not exists(sub-query) passed in values. Better to use CustomRepository.notExists method |
custom | string | Custom condition. For Server-side call only. For this condition expression can be any SQL statement |
Methods
# attrs (attrs: string) → CustomRepository instance
Adds attribute(s) or expression(s).
Can take expression as a field. In this case entity attribute name must be wrapped into [] brackets. In case of client-side execution the only valid expression is one of:
- 'SUM', 'COUNT', 'AVG', 'MAX', 'MIN', 'CAST', 'COALESCE'
- for PostGIS
ST_*
(Topological Relationships, Distance Relationships, Measurement Functions) are allowed. Restriction - first argument must be bracket attribute, oll other - either parameters or numeric constant
// chaining
UB.Repository('uba_user').attrs('ID').attrs('name', 'firstName').attrs('disabled').selectAsObject()
// calculate sum over some attribute
UB.Repository('uba_user').attrs('SUM([disabled])').where('disabled', '=', true).selectScalar()
// In case of server-side execution any valid SQL expression is accepted by attr:
UB.Repository('uba_user').attrs('[ID] / 100 + 1').selectAsArray()
// JOIN `uba_userrole.roleID` is a attribute of type Entity. ORM choose `left join` in case attribute is `allowNull: true`
UB.Repository('uba_userrole').attrs(['userID', 'userID.name']).selectAsObject()
// todo Define a way to join for UNITY (@)
// get values for attribute of type MANY
UB.Repository('tst_maindata')
.attrs('ID', 'manyValue', 'manyValue.caption')
.where('code', '=', 'Code1')
.selectAsObject({'manyValue.caption': 'captions'})
// result is `[{"ID":331947938939292,"manyValue":"1,2","captions":"caption 10,caption 20"}]`
// Get attribute value for multi-language ("isMultiLang": true in meta file) attribute other when current session language
UB.Repository('org_employee').attrs(['ID', 'lastName_en^']).selectAsObject()
// PostGIS
UB.Repository('tstgeo_geometry').attrs('ID', 'ST_Area([geoLine])')
.where('ST_Intersects([geo], ?)', 'custom', '{"type":"LineString","coordinates":[[0,0],[0,2]]}')
.select()
# attrsIf (addingCondition: *, attrs: string) → CustomRepository instance
Helper method for CustomRepository.attrs.
Calls attrs
in case addingCondition is truthy
Arguments:
let isPessimisticLock = !!UB.connection.domain.get('uba_user').attributes.mi_modifyDate
// with whereIf
let repo = UB.Repository('uba_user').attrs('ID').attrsIf(isPessimisticLock, 'mi_modifyDate')
//without whereIf
let repo = UB.Repository('uba_user').attrs('ID')
if (isPessimisticLock) repo = repo.attrs('mi_modifyDate')
# chunksByID (callback: function, optionsopt: object) instance
Chunks given repository by options.chunkLength
calling a callback for each chunk.
To be used on massive operations over entity with potentially hure number of records.
Repository fieldList must include ID attribute, resulted rows are ordered by ID (all orderBy from repo are removed by method)
Arguments:
callback
: functionoptions
: objectchunkLength
=10000: numberchunk length. Default is 10_000
idToStartFrom
: numberoptional ID to start from
maxChunks
: numberoptional maximum numbers of chunks to call callback (max row count will be chunkLength*maxChunks). By default - callback called for all chunks
callbackUseData
: booleanIf
true
- passchunkData
as 3-d callback argument (slow), otherwise only startID and endID is passed
const docStore = UB.DataStore('doc_document')
function updateDocSQL(startID, endID, chunkData) {
// callback can do direct SQL execution to bypass all JS logic during migrations, for example
docStore.execSql(`update doc_document set mi_tr_notes = substring(notes, 0, 2000)
where notes is not null and mi_tr_notes is null and ID >= :startID: and ID <= :endID:`,
{ startID, endID })
App.dbCommit() // commit update for each chunk to prevent rollback logs to be huge
}
UB.Repository('doc_document').attrs('ID')
.where('notes', 'notIsNull')
.where('mi_tr_notes', 'isNull')
.chunksByID(updateDocSQL)
// use chunk data by passing { callbackUseData: true } to options and call a mixins methods
function updateDocJs(startID, endID, chunkData) {
chunkData.forEach((row) => {
//here row is object with attributes, passed in Repository fieldList
docStore.update({
execParams: { ID: row.ID, mi_modifyDate: row.mi_modifyDate, someAttr: 'someVal' })
})
})_
App.dbCommit()
}
UB.Repository('doc_document').attrs('ID', 'mi_modifyDate')
.where('notes', 'notIsNull')
.where('mi_tr_notes', 'isNull')
.chunksByID(updateDocJS, { callbackUseData: true })
# clearOrderList () → CustomRepository instance
Remove all sorting. This function mutates the current Repository
let repo1 = UB.Repository('uba_user').attrs('code', 'name).orderBy('name')
let repoWithoutOrder = repo1.clone().clearOrderList()
# clearWhereList () → CustomRepository instance
Remove all where conditions (except ones using in joinAs). This function mutates the current Repository
let repo1 = UB.Repository('uba_user').attrs('ID', 'code').where('ID', '>', 15, 'byID')
let repoWithoutWheres = repo1.clone().clearWhereList()
# clone () → CustomRepository instance
Creates a clone of this repository
let repo1 = UB.Repository('uba_user').attrs('ID', 'code').where('ID', '>', 15, 'byID')
let repo2 = repo1.clone()
repo1.orderBy('code')
repo2.attrs('name').where('ID', '>', 100, 'byID')
repo1.selectAsObject() // return ordered users with ID > 15
repo2.selectAsObject() // return unordered users with their names and ID > 100
# correlation (subQueryAttribute: string, masterAttribute: string, conditionopt: WhereCondition | string, clauseNameopt: string) → CustomRepository instance
If current repository is used as a sub-query for exists
, notExists
, in
or notIn
conditions
correlation with a master repository will added
# exists (subRepository: CustomRepository, clauseNameopt: string) → CustomRepository instance
Adds where condition with EXISTS
sub-query. Inside a sub-query there are two macros:
{master}
will be replaced by master entity alias{self}
will be replaced by sub-query entity alias
Arguments:
subRepository
: CustomRepositoryRepository, what represent a sub-query to be execute inside EXISTS statement
clauseName
: stringOptional clause name
UB.Repository('uba_user').attrs(['ID', 'name']) //select users
// who are not disabled
.where('disabled', '=', 0)
// which allowed access from Kiev
.where('trustedIP', 'in',
UB.Repository('geo_ip').attrs('IPAddr')
.where('city', '=', 'Kiev')
)
// who do not log in during this year
.notExists(
UB.Repository('uba_audit')
.correlation('actionUser', 'name') // here we link to uba_user.name
.where('actionTime', '>', new Date(2016, 1, 1))
.where('actionType', '=', 'LOGIN')
)
// but modify some data
.exists(
UB.Repository('uba_auditTrail')
.correlation('actionUser', 'ID') // here we link to uba_user.ID
.where('actionTime', '>', new Date(2016, 1, 1))
)
.select()
# groupBy (attr: string) → CustomRepository instance
Adds grouping
UB.Repository('my_entity').attrs('ID')
.groupBy('code')
UB.Repository('uba_user').attrs('disabled')
.groupBy('disabled').select()
UB.Repository('uba_user').attrs(['disabled','uPassword','COUNT([ID])'])
.groupBy(['disabled','uPassword']).select()
# join (whereItemName: string) → CustomRepository instance
Force where expressions
to be used in join part of SQL statement instead of where part. Applicable only for not cached entities
Arguments:
whereItemName
: stringname of where item to use in join.
// will generate
// SELECT A.ID, B.code FROM tst_document A LEFT JOIN tst_category B
// ON (B.instanceID = A.ID and B.ubUser = 10)
// instead of
// SELECT A.ID, B.code FROM tst_document A LEFT JOIN tst_category B
// ON B.instanceID = A.ID
// WHERE B.ubUser = 10
UB.Repository('tst_document').attrs(['ID', '[category.code]'])
.where('[category.ubUser]', '=', 10, 'wantInJoin')
.join('wantInJoin')
.selectAsObject().then(UB.logDebug)
# joinCondition (expression: string, condition: CustomRepository.WhereCondition, valuesopt: *, clauseNameopt: string) → CustomRepository instance
Adds join condition
Arguments:
expression
: stringAttribute name (with or without []) or valid expression with attributes in [].
condition
: CustomRepository.WhereConditionAny value from WhereCondition list.
values
: *Condition value. In case expression is complex can take {object} as value. In case values === undefined no values property passed to where list
clauseName
: stringOptional clause name to be used in {CustomRepository.logicalPredicates}. If not passed where will generate unique clause named 'c1', 'c2', ......
# limit (rowsLimit: number) → CustomRepository instance
How many rows to select. If 0 - select all rows started from .start()
Arguments:
rowsLimit
: number
// will return first two IDs from my_entity
let store = UB.Repository('my_entity').attrs('ID').limit(2).selectAsObject()
# logic (predicate: string) → CustomRepository instance
Arrange named where expressions
in logical order. By default, where expressions
are joined by AND logical predicate.
It is possible to join it in custom order using logic
.
Pay attention to condition name we pass as a 4-th parameter to .where()
Arguments:
predicate
: stringlogical predicate.
UB.Repository('my_entity').attrs('ID')
// code in ('1', '2', '3')
.where('code', 'in', ['1', '2', '3'], 'byCode')
// name like '%homer%'
.where('name', 'contains', 'Homer', 'byName')
//(birthday >= '2012-01-01') AND (birthday <= '2012-01-02')
.where('birthday', 'geq', new Date()).where('birthday', 'leq', new Date() + 10)
// (age + 10 >= 15)
.where('[age] -10', '>=', {age: 15}, 'byAge')
// (byCode OR byName) AND (all where items, not included in logic)
.logic('([byCode]) OR ([byName])')
# misc (flags: object) → CustomRepository instance
Apply miscellaneous options to resulting UBQL
Arguments:
flags
: object__mip_ondate
: DateSpecify date on which to select data for entities with
dataHistory
mixin. Default to Now()__mip_recordhistory
: booleanSelect only record history data for specified ID (for entities with
dataHistory
mixin)__mip_recordhistory_all
: booleanIgnore __mip_ondate and select all data (acts as select for entities without
dataHistory
mixin)__mip_disablecache
: booleanFor entities with cacheType in ["Session", "SessionEntity"] not check is data modified and always return result
__skipOptimisticLock
: booleanSkip optimistic lock for entities with
mStorage.simpleAudit = true
__allowSelectSafeDeleted
: booleanInclude softly deleted rows to the result
__useReplica
: booleanPrefer to use DB replica for select statement (if configured, EE only)
__skipSelectAfterUpdate
: booleanServer-side only
__skipSelectAfterInsert
: booleanServer-side only
__skipSelectBeforeUpdate
: booleanServer-side only if added then
mStorage
mixin don't executeselect
before execution ofupdate
. As a UNSAFE side effectupdate
won't check record is accessible to user. UNSAFE IfAudit
mixin is implemented for entity empty OldValue in inserted intouba_auditTrail
in case this flag is enabled, so better to think twice before skip select before update__skipRls
: booleanServer-side only
__skipAclRls
: booleanServer-side only
lockType
: stringFor entities with
softLock
mixin retrieve/set a lock during method execution Possible values:- 'None': get a lock info during select* execution (for a results with a single row)
Temp
orPersist
: set a lock (temp or persistent) together with select* execution (for a results with a single row)
// this server-side call will select all currency, including deleted
UB.Repository('cdn_currency').attrs(['ID'])
.misc({__allowSelectSafeDeleted: true}).selectAsArray();
# miscIf (addingCondition: *, flags: object) → CustomRepository instance
Helper method for CustomRepository.misc.
Calls misc
in case addingCondition is truthy
Arguments:
addingCondition
: *flags will be applied only in case addingCondition is truthy
flags
: object
# notExists (subRepository: CustomRepository, clauseNameopt: string) → CustomRepository instance
Adds where condition with NOT EXISTS
sub-query. See CustomRepository.exists for sample
Arguments:
subRepository
: CustomRepositoryRepository, what represent a sub-query to be execute inside EXISTS statement
clauseName
: stringOptional clause name
# orderBy (attr: string, directionopt: string) → CustomRepository instance
Sorting. If expression already exists in order list it direction will be changed or, in case direction === null it will be removed
Arguments:
let repo = UB.Repository('my_entity').attrs('ID').orderBy('code')
let orderedData = await repo.selectAsObject() // ordered. await is for client-side only
repo.orderBy('code', null) // remove order by code
let unorderedData = await repo.selectAsObject() // NOT ordered
# orderByDesc (attr: string) → CustomRepository instance
Adds descend sorting. The same as orderBy(attr, 'desc'). To remove such sorting call orderBy(attr, null)
Arguments:
attr
: string
UB.Repository('my_entity').attrs('ID')
// ORDER BY code, date_create DESC
.orderBy('code').orderByDesc('date_create')
# select (instanceopt: TubDataStore) → TubDataStore instance
Arguments:
instance
: TubDataStoreOptional instance for in-thread execution context. If passed - run select for it (not create new instance) and return instance as a result.
# selectAsArray (resultInPlainTextopt: Boolean) → TubCachedData | String instance
Return:
// todo this is TubCachedData structure!!!
Arguments:
resultInPlainText
= false: BooleanIf true - result is {String}
# selectAsArrayOfValues () → Array.<(string | number)> instance
For repository with ONE attribute returns a flat array of attribute values
const usersIDs = UB.Repository('uba_user'),attrs('ID').limit(100).selectAsArrayOfValues()
// usersIDs is array of IDs [1, 2, 3, 4]
# selectAsObject (fieldAliasesopt: Object.<string, string>, resultInPlainTextopt: boolean) → Array.<object> | string instance
# selectAsStore (instanceopt: TubDataStore) → TubDataStore | Array.<Object> instance
Create new, or use passed as parameter TubDataStore and run TubDataStore.select method passing result of CustomRepository.ubql() as config. Do not work for remote connection.
Arguments:
instance
: TubDataStoreOptional instance for in-thread execution context. If passed - run select for it (not create new instance) and return instance as a result. Be careful - method do not check instance is created for entity you pass to Repository constructor.
# selectById (ID: Number, fieldAliasesopt: Object.<string, string>) → Object | undefined instance
Select a single row by ID. If ubql result is empty - return {undefined}.
# selectScalar () → Number | String | undefined instance
Perform select and return a value of the first attribute from the first row
WARNING method do not check repository contains the single row
# selectSingle (fieldAliasesopt: Object.<string, string>) → Object | undefined instance
Select a single row. If ubql result is empty - return {undefined}.
WARNING method do not check repository contains the single row and always return a first row from result
# start (start: number) → CustomRepository instance
Retrieve first start
rows
Arguments:
start
: number
let store = UB.Repository('my_entity').attrs('ID')
//will return ID's from 15 to 25
.start(15).limit(10).select()
# ubql () → UBQL instance
Construct a UBQL JSON
let repo = UB.Repository('my_entity').attrs('ID').where('code', '=', 'a')
let inst = UB.DataStore(my_entity)
inst.run('select', repo.ubql())
# using (methodName: string) → CustomRepository instance
Retrieve data from server using methodName
entity method.
By default, select
method will be used
Arguments:
methodName
: string
# where (expression: string, condition: CustomRepository.WhereCondition | string, valueopt: *, optionsopt: object) → CustomRepository instance
Adds where expression
-
the expression may contain one of the following functions: 'SUM', 'COUNT', 'AVG', 'MAX', 'MIN', 'CAST', 'COALESCE', 'LENGTH', 'LOWER', 'UPPER', 'DAY', 'MONTH', 'YEAR', 'ROUND', 'FLOOR', 'CEILING'
-
for PostGIS
ST_*
(Topological Relationships, Distance Relationships, Measurement Functions) are allowed. Restriction - first argument must be bracket attribute, oll other - either parameters or numeric constant -
for a Date/DateTime attributes special macros
#maxdate
or#currentdate
can be used as a value:.where('dateValue', '=', '#maxdate') .where('dateTimeValue', '<', '#currentdate')
-
in
and 'notIn` conditions can take a sub-repository as a value parameter value. See CustomRepository.exists for sample -
for details how array parameters binds to DB query see array binding section in database tuning tutorial
Arguments:
expression
: stringAttribute name (with or without []) or valid expression with attributes in []
condition
: CustomRepository.WhereCondition| stringAny value from WhereCondition
value
: *Condition value. If
undefined
value not passed to ubqloptions
: object| stringclauseName
: stringclause name to be used in {CustomRepository.logicalPredicates}
- If not passed unique clause name will be generated ('_1', '_2', ..).
- In case a condition with the same name exists, it will be overwritten.
clearable
: booleanif === false then clearWhereList() will skip removing this where condition
If string is passed it means
clauseName
, otherwise an object {clauseName, clearable}
UB.Repository('my_entity').attrs('ID')
// code in ('1', '2', '3')
.where('code', 'in', ['1', '2', '3'])
// code in (select code from my_codes where id = 10)
.where('code', 'in', UB.Repository('my_codes').attrs('code').where('ID', '<', 10))
// name like '%homer%'
.where('[name]', 'contains', 'Homer')
//(birtday >= '2012-01-01') AND (birtday <= '2012-01-02')
.where('[birtday]', 'geq', new Date()).where('birtday', 'leq', new Date() + 10)
// (age + 10 >= 15)
.where('[age] -10', '>=', {age: 15}, 'byAge')
.where('LENGTH([code])', '<', 5)
// for condition match expression not need
.where('', 'match', 'myvalue')
# whereIf (addingCondition: *, expression: string, condition: CustomRepository.WhereCondition | string, valuesopt: *, clauseNameopt: string) → CustomRepository instance
Helper method for CustomRepository.where.
Calls where
in case addingCondition is truthy
Arguments:
addingCondition
: *Where expression will be added only in case addingCondition is truthy
expression
: stringAttribute name (with or without []) or valid expression with attributes in []
condition
: CustomRepository.WhereCondition| stringAny value from WhereCondition
values
: *Condition value. If
undefined
values not passed to ubqlclauseName
: stringOptional clause name to be used in {CustomRepository.logicalPredicates} If not passed where will generate unique clause named 'c1', 'c2', ......
let filterString = 'foundAllLikeThis' // or may be empty string
// with whereIf
let repo = UB.Repository('my_entity').attrs('ID').whereIf(filterString, 'myAttr', 'like', filterString)
//without whereIf
let repo = UB.Repository('my_entity').attrs('ID')
if (filterString) repo = repo.where('myAttr', 'like', filterString)
# withTotal (valueopt: boolean) → CustomRepository instance
Calculate total row number. WARNING!! This is VERY slow operation on DB level in case of many record
Result of calculation is returned in __totalRecCount parameter value in case selectAsArray()
client call:
Arguments:
value
= true: booleanIf
false
will remove total requirements
let result = UB.Repository('uba_user')
.attrs(['ID', 'description'])
.withTotal().selectAsArray();
console.log('Total count is:', result.__totalRecCount)
// Or into TubDataStore.totalRowCount in case of server side `selectAsStore()` call:
let store = UB.Repository('uba_user')
.attrs(['ID', 'description'])
.withTotal().selectAsStore();
console.log('Total count is:', store.totalRowCount);