Class for execution of an ORM/SQL queries on the server side. Contains several data collection stored in the heap of ub application (i.e not use a JS engine memory and since not a subject of GC)
Use it to:
- execute any entity method using TubDataStore.run
- execute any SQL statement using TubDataStore.runSQL or TubDataStore.execSQL (we strongly recommend usage of ORM instead SQL)
- store several named data collection using TubDataStore.currentDataName (data stored inside server memory, not in JS, this is very good for GC)
- iterate other collection rows using TubDataStore.next, eof, e.t.c and retrieve row data using TubDataStore.get
- serialize data to XML TubDataStore.asXMLPersistent or JSON string in array-of-array TubDataStore.asJSONArray or array-of-object TubDataStore.asJSONObject format
- serialize data to JavaScript object in array-of-array TubDataStore.getAsJsArray() or array-of-object TubDataStore.getAsJsObject() format
To retrieve data from database using build-in ORM (execute entity select
method) preferred way is
to use UB.Repository fabric function.
# new TubDataStore ()
Members
# DATA_NAMES static
Active dataset name we work with
let store = ctx.dataStore
let prevData = store.currentDataName
try {
store.currentDataName = TubDataStore.DATA_NAMES.BEFORE_UPDATE
let valueBeforeUpdate = store.get('code')
} finally {
store.currentDataName = prevData
}
# asJSONArray : string deprecated instance
Return string representation of Instance in Array of array
format
Consider to replace `JSON.parse(store.asJSONArray) -> store.getAsJsArray()` which returns a plain JS object instead of string and 25% faster
# asJSONObject : string deprecated instance
Return string representation of Instance in format [{attr1: value1, attr2: value2},... ]
Consider to replace JSON.parse(store.asJSONObject) -> store.getAsJsObject(). getAsJsObject() method return a plain JS object instead of string and 25% faster
# asXMLPersistent : string instance
Return XML representation of Instance in MS DataSet format
# bof : boolean instance
Indicate current position in data collection is on the beginning of collection
# currentDataName : string instance
Active dataset name we work with. There is some predefined dataNames - see TubDataStore.DATA_NAMES
# entity : UBEntity instance
Entity metadata
# eof : boolean instance
Indicate current position in data collection is on the end of collection.
# getAsJsArray : string instance
Return JavaScript Object representation of Instance in Array of array
format
# getAsJsObject : string instance
Return JavaScript Object representation of Instance in format [{attr1: value1, attr2: value2},... ]
# initialized : boolean instance
Is store initialized
# rowCount : Number instance
Last store operation row count
- for operations what returns result (
select
) - fetched record count - for non-select operations - affected row count (deleted, updated, etc)
- if DataStore is just created - return 0 NOTE before UB@5.22.3 property returns only fetched record count, for update/delete returns 0
# rowPos : Number instance
Row position inside currentDataName dataset. Read/write
# totalRowCount : Number instance
Total record count if store are filled with Repository.withTotal() option. If DataStore is not initialized or empty or initialized without withTotal() will return -1.
Methods
# commitBLOBStores (ctx: ubMethodParams, isUpdate: boolean) → boolean static
For modified attributes of type Document
:
- call a BLOB store implementation method
moveToPermanent
- sets a BLOB attribute value in execParams to permanent blob info
This method is called by native
insert
/update
/delete
before database modifications
Return:
True in case some attributes of document type are actually changed
Arguments:
ctx
: ubMethodParamsisUpdate
: boolean
# initialize (source: object | Array, keyMapopt: Array.<(string | object)>) → TubDataStore static
Initialize DataStore from one of supported source formats:
- Flatten(fastest):
{fieldCount: K, rowCount: Z, values: [field1Name, ..., fieldKName, row1field1Value, ..., row1fieldKValue, row2field1Value,..]}
- Array-of-array : '[[row1field1Value, ..., row1fieldKValue], ..., [rowZfield1Value, ... rowZfieldKValue]'
- Array-of-object : '[{field1Name: row1field1Value, ..., fieldKName: row1fieldKValue}, ....]'
Can (optionally) convert source field names to new names using keyMap array.
Arguments:
const UB = require('@unitybase/ub')
var ds = UB.DataStore('my_entity')
// init empty (rowCount=0) dataStore with provided fields.
// In case keyMap is omitted we consider it contains one attribute 'ID'
ds.initialize([]) // the same as ds.initialize([], ['ID'])
ds.initialize([], ['ID', 'name', {from: 'AGE', to: 'age'}])
// Initialize dataStore from array-of-object representation
// Resulting dataStore will contain 3 field: ID, nam, age (in order, they listen in keyMap array).
// During initialization we convert fiend name 'AGE' -> age;
ds.initialize([
{ID: 10, name: 'Jon', AGE: 10},
{ID: 20, name: 'Smith', AGE: 63}
],
['ID', 'name', {from: 'AGE', to: 'age'}]
)
//the same, but do not convert AGE->age. Result dataset field order is unknown
ds.initialize([
{ID: 10, name: 'Jon', AGE: 10},
{ID: 20, name: 'Smith', AGE: 63}
])
//result dataset will contain only two field 'ID' & 'age'
ds.initialize([
{ID: 10, name: 'Jon', AGE: 10},
{ID: 20, name: 'Smith', AGE: 63}
],
['ID', {from: 'AGE', to: 'age'}]
)
// Initialize dataStore from Array-of-array data
// in this case keyMap is mandatory.
// In case of mapping from is zero-based index of source element in row array
ds.initialize(
[[10, 'Jon', 10], [20, 'Smith', 63]],
['ID', 'name', 'age']
)
// or use mapping
ds.initialize([[10, 'Jon', 10], [20, 'Smith', 63]],
['ID', {from: 2, to: 'age'}, {from: 1, to: 'name'}])
# insert (ubq: ubRequest) → null | number | Array static
Execute insert method by add method: 'insert' to ubq
query (if ubq.method not already set).
The same semantic as in SyncConnection.insert
and AsyncConnection.insert
If ubq.fieldList
contain only ID
return inserted ID, else return array of attribute values passed to fieldList
.
If no field list passed at all - return null.
Arguments:
ubq
: ubRequest
const STORE = UB.DataStore('uba_role')
// return array of values in order specified ib fieldList
// result is [3000000000200,"2014-10-21T11:56:37Z"].
// Below we use destructuring assignment to get array values into variables,
// so ID === 3000000000200 and modifyDate === "2014-10-21T11:56:37Z"
const [ID, modifyDate] = STORE.insert({
fieldList: ['ID', 'mi_modifyDate'],
execParams: {
name: 'testRole1',
allowedAppMethods: 'runList'
}
})
// return ID (if ID not passed in execParamms it will be generated)
// 3000000000201
const testRoleID = STORE.insert({
fieldList: ['ID'],
execParams: {
name: 'testRole1',
allowedAppMethods: 'runList'
}
})
// no fieldList - null is returned.
// This is faster compared to inserts with fieldList because selectAfterInsert is skipped
STORE.insert({
execParams: {
name: 'testRole1',
allowedAppMethods: 'runList'
}
})
# insertAsObject (ubq: ubRequest, fieldAliasesopt: object.<string, string>) → object | null static
Run UBQL command with insert
method. WARNING better to use insert method - it is faster because values is not parsed.
In case fieldList
is passed - result will contain new values for attributes specified in fieldList
as Object, otherwise - null
In opposite to insert
method values in result are PARSED based on Domain (as in AsyncConnection) - so values
for boolean attributes is true/false, date is typeof Date etc.
Arguments:
const STORE = UB.DataStore('uba_role')
const newRole = conn.insertAsObject({
entity: 'uba_role', // can be omitted - we already define entity in STORE constructor
fieldList: ['ID', 'name', 'allowedAppMethods', 'mi_modifyDate'],
execParams: {
name: 'testRole61',
allowedAppMethods: 'runList'
}
}, {mi_modifyDate: 'modifiedAt'})
console.log(newRole) // {ID: 332462911062017, name: 'testRole1', allowedAppMethods: 'runList', mi_modifyDate: 2020-12-21T15:45:01.000Z}
console.log(newRole.modifiedAt instanceof Date) //true
# update (ubq: ubRequest) → * static
Execute update
method. The same semantic as in SyncConnection.update
and AsyncConnection.update
If no field list passed - return null (this is faster), else return array of attribute values passed to fieldList
.
Arguments:
ubq
: ubRequest
# updateAsObject (ubq: ubRequest, fieldAliasesopt: object.<string, string>) → object | null static
Execute update
method. The same semantic as in SyncConnection.updateAsObject
and AsyncConnection.updateAsObject
- If fieldList in the ubq not passed or is an empty array - returns null
- If fieldList passed in the ubq, values in result are PARSED based on Domain (as in AsyncConnection) - so values for boolean attributes is true/false, date is typeof Date etc.
# execSQL (sql: string, params: Object) → number instance
Execute SQL with parameters. Not wait result data
Return:
Affected rows count (since UB@5.22.3, before returns false)
# fieldIndexByName (fieldName: string) instance
Return zero based index of fieldName from current data store (-1 if not found)
Arguments:
fieldName
: string
let r = UB.Repository('cdn_organization').attrs(['ID', 'mi_owner.name'])
.where('[ID]', '=', 3000000002801)
.select()
console.log(r.fieldIndexByName('mi_owner.name')) // 1
console.log(r.fieldIndexByName('unexistedAttr')) // -1
# first () instance
Move first
# freeNative () instance
Release all internal resources. Store became unusable after call to freeNative()
# generateID () → Number instance
Generate a new identifier (int64)
# get (attrib: Number) → Number | String instance
Return value of attribute.
In case store initialized using TubDataStore.run style we can return Number or String type, but in case it initialized using runSQL columns data types is unknown and you must cast value do required type directly.
# getAsBuffer (attrib: Number) → ArrayBuffer instance
Return value of attribute as ArrayBuffer.
You can apply this method to blob fields only
# initFromJSON (source) instance deprecated
Use `TubDataStore.initialize` instead
init dataStore content from JSON string. WARNING!!! during initFromJSON UnityBase determinate field types from vield values, so if some data column contain only numeric values it becode Number (even if in source it String).
Arguments:
source
:
const UB = require('@unitybase/ub')
let ds = UB.DataStore('myEntityCode')
ds.initFromJSON({"fieldCount":1,"values":["ID"],"rowCount":0});
console.log(ds.initialized); // TRUE
# last () instance
Move last
# next () instance
Move next
# prev () instance
Move prev
# run (methodName: string, params: Object) → boolean instance
Run any entity method.
Return:
True in case of success, else raise exception
const store = new TubDataStore('doc_attachment');
store.run('update', {
execParams: {
ID: 1,
approved: 0
}
})
store.run('anyEntityMethod', {param1: 'valueOfParam1', ...})
# runSQL (sql: string, params: Object | TubList, useReplicaopt: boolean) instance
Execute SQL with parameters and place result into dataStore. This method expect SQL statement have result.
To execute SQL statement without result (insert
for example) - use TubDataStore.execSQL instead.
# runWithResult (methodName: string, params: Object) → Object | TubList instance
The same as store.run
, but params, passed to method is returned as a result.
Can be used to call methods, what modify a params.
WARNING - recommended way to create a server-side method what should return a result and can be called from both server-side and client-side is to create TWO function - one for server-side usage, and a wrapper for client-side usage:
Return:
params (maybe modified by method call)
// method what modify an mParams
tst_entity.multiply = function(ctx) {
const a = ctx.mParams.a; const b = ctx.mParams.b;
ctx.mParams.multiplyResult = a * b
}
tst_entity.entity.addMethod('multiply')
// call method and get a result (modified mParams)
const store = new TubDataStore('tst_service');
const modifiedParams = store.runWithResult('multiply', { a: 3, b: 10 })
console.log(modifiedParams) // { a: 3, b: 10, multiplyResult: 30 }
# setColumnName (columnIdx: number, newColumnName: string) instance
Set name for column. New name will be used during serialization to JSON or response
// change column name in HTTP response for select method
entity.on('select:after', ctx => {
const ccIdx = ctx.mParams.fieldList.indexOf('category.code')
if (ccIdx !== -1) {
ctx.dataStore.setColumnName(ccIdx, 'categoryCode')
}
})
// change column name in HTTP response for custom method
entity.customSelect = function (ctx) {
UB.Repository('tst_document').attrs('ID', 'category.code').limit(10).select(ctx.dataStore)
ctx.dataStore.setColumnName(1, 'categoryCode')
// caller got categoryCode instead of category.code
}
// change column name for custom SQL
store = new UB.DataStore('my_entity')
store.runSQL('select 1 from dual', {})
store.setColumnName(0, 'dummy')
const obj = store.getAsJsObject() // obj will be [{dummy: 1}]
# switchEntity (newContextEntityCode: string) instance
Change an entity associated with DataStore. Example shows method for uba_user what actually return a result from FTS connection
Arguments:
newContextEntityCode
: string
const me = uba_user
me.runInAnotherConn = function (ctx) {
ctx.dataStore.runSQL('select * from uba_user', {})
ctx.dataStore.switchEntity('fts_ftsDefault')
ctx.dataStore.runSQL('SELECT A01.ID,A01.entity,A01.ftsentity,A01.databody FROM ftsDefault_en A01 LIMIT 10 OFFSET 0', {})
}