# connect
connect (path: string, options?: TrilogyOptions): Trilogy
Initialize a new datastore instance, creating a SQLite database file at
the provided path
if it does not yet exist, or reading it if it does.
trilogy can use both the native sqlite3
module or sql.js
— the
latter does not require compiling, whereas the former must be built against
its runtime environment. The goal is that the difference should be totally
invisible to you, so usage from trilogy's standpoint stays the same no
matter which one you choose. You should also be able to change it at any
time without any hitches.
If path
is exactly ':memory:'
, no file will be created and an in-memory
store will be used. This doesn't persist any of the data.
See "Backends" for more about the backend options trilogy supports and some guidance on which to choose.
ARGUMENTS
{string} path
: absolute or relative file path. If relative it is resolved againstprocess.cwd()
, or theoptions.dir
property if it is provided.- optional
{TrilogyOptions} options
:
property | type | default | description |
---|---|---|---|
client | string | sqlite3 | Must be one of sqlite3 or sql.js . |
dir | string | process.cwd() | The working directory with which to resolve path . |
RETURNS
USAGE
import { connect } from 'trilogy'
// defaults to using `sqlite3`
const db = connect('./storage.db')
With an options object:
const db = connect('./storage.db', {
// optionally use `sql.js`, eg. to avoid native module build issues
client: 'sql.js',
// directory with which to resolve `path`
// it defaults to `process.cwd()`
dir: path.resolve('./here')
})
THROWS
if path
is not provided
# Trilogy (Class
)
# model
model <D> (name: string, schema: SchemaRaw, options?: ModelOptions):
Promise<Model>
TIP
If you're using TypeScript, you should provide your own type as the
generic D
, which will allow for much more powerful typechecking! If
you're using JavaScript you can safely ignore the additional <D>
syntax and think of D
as being any old object
.
Define a new model with the provided schema
, or return the existing model
if one is already defined with name
.
Each property of schema
describes a column, where its key is the name of
the column and its value describes its attributes. The value can be either
a type, such as String
, Number
, or 'increments'
, or a more descriptive
object. See "Property descriptors"
for more information.
This schema controls the handling of values inserted into and retreived from the database, casting them as needed. For example, SQLite does not support a boolean data type and instead stores them as integers. trilogy will transparently cast booleans to integers when inserting them and back to booleans when retreiving them. The same goes for all other supported data types. Unknown column types will cause an error to be thrown.
If any property of schema
is not present in knex's methods it will be
ignored. See knex's documentation (opens new window)
on Schema Building for the available attributes when defining properties.
ARGUMENTS
{string} name
: name of the model{SchemaRaw} schema
: describes the schema of the table- optional
{ModelOptions} options
:
property | type | default | description |
---|---|---|---|
timestamps | boolean | - | When true , adds created_at and updated_at properties, both defaulting to the current timestamp. |
primary | string[] | - | An array of column names to specify as a composite primary key. |
unique | string[] | - | An array of column names on which to apply unique constraints. |
index | string , string[] , Array<string[]> , object | - | See "Complex indexing" |
WARNING
Specifying a column as either primary
or unique
in both the column
descriptor and the options.primary
or options.unique
arrays will result
in an error, as the constraint will have already been applied.
RETURNS
Promise<
Model
<D>>
USAGE
await db.model('people', {
name: String,
age: Number,
email: String,
uid: { type: Number, primary: true }
})
await db.find('people', { /* ... */ })
await db.create('people', { /* ... */ })
await db.min('people.age', { /* ... */ })
// already defined, so it's returned
const people = await db.model('people')
// since `model()` returns a model instance,
// you can use methods on that instance like so:
await people.find({ /* ... */ })
await people.create({ /* ... */ })
await people.min('age', { /* ... */ })
# getModel
getModel <D> (name: string): Model
Provides a way to synchronously retrieve a model. If that model doesn't
exist an error will be thrown, so you should only use this if you're sure
it has been defined. Or you can check first with hasModel
or use model
, which returns existing definitions.
ARGUMENTS
{string} name
: name of the model
RETURNS
USAGE
db.getModel('people')
// Error: no model defined by the name 'people'
const people = await db.model('people', {
name: String
})
const alsoPeople = db.getModel('people')
alsoPeople.find(/*...*/)
// these reference the exact same object (the `Model`)
people === alsoPeople
// -> true
THROWS
if no model by the name of name
has been created
# hasModel
hasModel (name: string): Promise<boolean>
First checks if the model's been defined with trilogy, then runs an existence
query on the database, returning true
if the table exists or false
if it
doesn't.
ARGUMENTS
{string} name
: the name of the model to check
RETURNS
Promise<boolean>
USAGE
const exists = await db.hasModel('people')
if (exists) {
console.log('it exists!')
}
# dropModel
dropModel (name: string): Promise<boolean>
Removes the specified model from trilogy's definition and the database.
ARGUMENTS
{string} name
: the name of the model to remove
RETURNS
Promise<boolean>
: true
if successful or false
if the model was not
defined with trilogy
USAGE
await db.dropModel('people')
// model & table dropped
# raw
raw (query: knex.QueryBuilder | knex.Raw, needResponse?: boolean): Promise<any>
Allows running any arbitrary query generated by trilogy's .knex
instance. If the result is needed, pass true
as the second argument, otherwise
the number of affected rows will be returned ( if applicable ).
ARGUMENTS
{knex.QueryBuilder | knex.Raw} query
: any knex query created with.knex
{boolean} needResponse
: whether to return the result of the query
RETURNS
Promise<any>
USAGE
const query = db.knex('users')
.innerJoin('accounts', function () {
this.on('accounts.id', '=', 'users.account_id')
.orOn('accounts.owner_id', '=', 'users.id')
})
db.raw(query, true).then(result => {})
# close
close (): Promise<void>
Drains the connection pools and releases connections to any open database files. This should always be called at the end of your program to gracefully shut down, and only once since the connection can't be reopened.
ARGUMENTS
None
RETURNS
Promise<void>
USAGE
import { connect } from 'trilogy'
const db = connect('./file.db')
db.find('title', ['release', '>', 2001])
.then(titles => {
return titles.map(title => {
return doSomething(title)
})
})
// sometime later ... program ending ...
db.close().then(() => {
// database connection ended
})
# models
models: string[]
An array of all model names defined on the instance.
# knex
Exposes trilogy's knex instance to allow more complex query building. You can use this to create queries that aren't necessarily feasible with trilogy's API, like nested where clauses that require functions as arguments.
DANGER
It is highly recommended that you do not call then
or otherwise execute
the query. Pass the knex query object as-is to raw()
for execution.
This ensures maximum compatibility between backends.
All of the following methods will cause execution when chained to a knex query, so avoid using these when building raw queries you intend to run with trilogy:
- Promises:
then
,catch
,tap
,map
,reduce
,bind
,return
- Callbacks:
asCallback
- Streams:
stream
,pipe
The following methods shouldn't cause problems, but they aren't
guaranteed to work, especially when using sql.js
:
- Events:
on
['query'
,'query-error'
,'query-response'
]
And finally the following methods will work just fine, but you should not chain them on a query object before passing it to trilogy. Running them separately is fine:
- Other:
toString
,toSQL
For more advanced usage, see knex's own documentation (opens new window).
USAGE
import { connect } from 'trilogy'
const db = connect('./file.db')
const query = db.knex('users').select('*')
console.log(query.toString())
// -> 'select * from `users`'
db.raw(query, true).then(users => {})
More complex queries are possible this way:
const query = db.knex('users')
.innerJoin('accounts', function () {
this.on('accounts.id', '=', 'users.account_id')
.orOn('accounts.owner_id', '=', 'users.id')
})
db.raw(query, true).then(result => {})
Don't do the following, as it will cause query execution.
db.knex('users').select('*')
.then(() => {})
# Model (Class
)
Model instances are created using model()
. Almost every model
instance method is also accessible at the top level of a trilogy instance,
meaning the following calls are equivalent:
// given this setup:
const db = connect('./storage.db')
const users = await db.model('users', { /* some schema */ })
// these are equivalent:
await db.find('users', { name: 'citycide' })
await users.find({ name: 'citycide' })
EXCEPTIONS
These model instance methods don't exist on database instances:
The function signatures remain the same except you provide the model name and,
in some cases, the column name, as the first argument. Column names should be
passed along with the table in dot-notation, ie. 'users.name'
.
If you're using TypeScript, you should provide a type to the model constructor to make typechecking much more powerful:
const db = connect(':memory:')
type Game = {
name: string,
players: number,
is_card: boolean,
is_dice: boolean
}
;(async () => {
// provide the `Game` type to `model()` here
const games = await db.model<Game>('games', {
name: String,
players: Number,
is_card: Boolean,
is_dice: Boolean
})
// this could fail at compile time if it didn't satisfy the provided type
await games.create({
name: 'Solitaire',
players: 1,
is_card: true,
is_dice: false
})
// type is inferred here as `Game[]`
const allGames = await games.find()
// you get suggestions and type checking here
console.log(allGames[0].players)
// -> 1
})()
If you don't provide a type, a loose default is used that prevents strong type safety.
TIP
For TypeScript users, Model
classes can be constructed with a generic type
that determines how their methods are typechecked. That type is represented as
a D
in this documentation.
# create
create (object: D, options?: CreateOptions): Promise<D>
Insert an object into the table. object
should match the model's defined
schema, values will cast into types as needed. If a unique or primary
constraint exists on a property the insert will be ignored when violating
this constraint.
ARGUMENTS
{D} object
: the data to insert- optional
{CreateOptions} options
property | type | default | description |
---|---|---|---|
raw | boolean | false | If true , will bypass getters & setters. |
RETURNS
Promise<D>
: the created object
USAGE
players.create({
id: 197397332,
username: 'xX420_sniperXx',
friends: ['xX420_kniferXx']
}).then(object => {
console.log(object)
// -> { id: 197397332,
// username: 'xX420_sniperXx',
// friends: ['xX420_kniferXx'] }
})
# find
find (criteria?: Criteria<D>, options?: FindOptions): Promise<D[]>
Find all objects matching criteria
.
ARGUMENTS
- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{FindOptions} options
:
property | type | default | description |
---|---|---|---|
random | boolean | false | Select a single random record. |
order | string , [string, string] | - | Specify a selection order. See the knex docs (opens new window). |
limit | number | - | Limit the rows returned. |
skip | number | - | Skip (offset) a number of records. |
raw | boolean | false | If true , will bypass getters & setters. |
Note: if options.random
is provided, options.order
is ignored.
RETURNS
Promise<D[]>
: array of found objects
USAGE
const todos = await db.model('todos', {
name: String,
body: String,
priority: Number
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
const found = await todos.find('name', ['priority', '<', 3])
console.log(found.map(todo => todo.name))
// -> ['code', 'docs']
# findIn
findIn (
column: string, criteria?: Criteria<D>, options?: FindOptions
): Promise<(D[keyof D])[]>
Find all objects matching criteria
and extract the given column from each one.
ARGUMENTS
{string} column
: the column to select from matching objects- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{FindOptions} options
:
property | type | default | description |
---|---|---|---|
random | boolean | false | Select a single random record. |
order | string , [string, string] | - | Specify a selection order. See the knex docs (opens new window). |
limit | number | - | Limit the rows returned. |
skip | number | - | Skip (offset) a number of records. |
raw | boolean | false | If true , will bypass getters & setters. |
Note: if options.random
is provided, options.order
is ignored.
RETURNS
Promise<(D[keyof D])[]>
: value at column
from each matching object
USAGE
const todos = await db.model('todos', {
name: String,
body: String,
priority: Number
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
const found = await todos.findIn('name', ['priority', '<', 3])
console.log(found)
// -> ['code', 'docs']
# findOne
findOne (criteria?: Criteria<D>, options?: FindOptions): Promise<D>
Find a single object.
ARGUMENTS
- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{FindOptions} options
:
property | type | default | description |
---|---|---|---|
random | boolean | false | Select a single random record. |
order | string , [string, string] | - | Specify a selection order. See the knex docs (opens new window). |
skip | number | - | Skip (offset) a number of records. |
raw | boolean | false | If true , will bypass getters & setters. |
Note: if options.random
is provided, options.order
is ignored.
RETURNS
Promise<D>
: the found object
USAGE
const todos = await db.model('todos', {
name: String,
body: String,
priority: Number
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
const found = await todos.findOne({ name: 'docs' })
console.log(found.body)
// -> 'crap what did I create'
# findOneIn
findOneIn (
column: string, criteria?: Criteria<D>, options?: FindOptions
): Promise<D[keyof D]>
Find a single object.
ARGUMENTS
{string} column
: the column to select from the matching object- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{FindOptions} options
:
property | type | default | description |
---|---|---|---|
random | boolean | false | Select a single random record. |
order | string , [string, string] | - | Specify a selection order. See the knex docs (opens new window). |
skip | number | - | Skip (offset) a number of records. |
raw | boolean | false | If true , will bypass getters & setters. |
Note: if options.random
is provided, options.order
is ignored.
RETURNS
Promise<D[keyof D]>
: value at column
of the found object
USAGE
const todos = await db.model('todos', {
name: String,
body: String,
priority: Number
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
const found = await todos.findOneIn('name', { priority: 1 })
console.log(found)
// -> { name: 'code',
// body: 'create the best thing',
// priority: 1 }
# findOrCreate
findOrCreate (
criteria: Criteria<D>, creation: Partial<D>, options?: FindOptions
): Promise<D>
Find a matching object based on criteria
, or create it if it doesn't exist.
When creating the object, a merged object created from criteria
and creation
will be used, with the properties from creation
taking precedence.
ARGUMENTS
{Criteria<D>} criteria
: criteria to search for{Partial<D>} creation
: data used to create the object if it doesn't exist- optional
{FindOptions} options
: same asfindOne()
RETURNS
Promise<D>
: the found object, after creation if necessary
USAGE
const people = await db.model('people', {
name: { type: String, primary: true },
age: Number,
adult: Boolean
})
const person = await people.findOrCreate(
{ name: 'Joe' },
{ age: 13, adult: false }
)
console.log(person)
// -> { name: 'Joe', age: 13, adult: false }
const person2 = await people.findOrCreate(
{ name: 'Joe' },
{ age: 99, adult: true }
)
// 'Joe' already exists and is returned
console.log(person2)
// -> { name: 'Joe', age: 13, adult: false }
# update
update (
criteria?: Criteria<D>, data?: Partial<D>, options?: UpdateOptions
): Promise<number>
Modify the properties of an existing object. While optional, if data
contains
no properties no update queries will be run.
ARGUMENTS
- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{Partial<D>} data
: the updates to be made - optional
{UpdateOptions} options
property | type | default | description |
---|---|---|---|
raw | boolean | false | If true , will bypass getters & setters. |
RETURNS
Promise<number>
: the number of rows affected
USAGE
const games = await db.model('games', {
name: { type: String, primary: true },
genre: String,
owned: Boolean
})
await games.create({ name: 'Overwatch', genre: 'FPS', owned: false })
const rowsAffected = await games.update(
{ name: 'Overwatch' },
{ owned: true }
)
console.log(rowsAffected)
// -> 1
# updateOrCreate
updateOrCreate (
criteria: CriteriaObj<D>,
data: Partial<D>,
options?: UpdateOptions & CreateOptions
): Promise<number>
Update an existing object or create it if it doesn't exist. If creation
is necessary a merged object created from criteria
and data
will be
used, with the properties from data
taking precedence.
ARGUMENTS
{CriteriaObj<D>} criteria
: criteria to search for{Partial<D>} data
: updates to be made, or used for object creation- optional
{UpdateOptions & CreateOptions} options
: same asupdate()
RETURNS
Promise<number>
: the number of rows affected
USAGE
const games = await db.model('games', {
name: { type: String, primary: true },
genre: String,
owned: Boolean
})
const rowsUpdated = await games.updateOrCreate(
{ name: 'Ms. PacMan' },
{ owned: false, genre: 'arcade' }
)
// -> 1
await games.findOne({ name: 'Ms. PacMan' })
// -> { name: 'Ms. PacMan', owned: false, genre: 'arcade' }
# get
get (
column: string, criteria?: Criteria<D>, defaultValue?: D[keyof D]
): Promise<D[keyof D]>
Works similarly to the get
methods in lodash, underscore, etc. Returns
the value at column
or, if it does not exist, the supplied defaultValue
.
ARGUMENTS
{string} column
: the property to retrieve- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{D[keyof D]} defaultValue
: returned if the result doesn't exist
RETURNS
Promise<D[keyof D]>
USAGE
const todos = await db.model('todos', {
name: String,
body: String,
priority: Number
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
await todos.get('priority', { name: 'code' })
// -> 1
// with default value
await todos.get('priority', { name: 'eat' }, 999)
// -> 999
# set
set (
column: string, criteria: Criteria<D>, value: D[keyof D]
): Promise<number>
Works similarly to the set
methods in lodash, underscore, etc. Updates
the value at column
to be value
where criteria
is met.
ARGUMENTS
{string} column
: the column to update{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
{D[keyof D]} value
: the new value
RETURNS
Promise<number>
: the number of rows affected
USAGE
const todos = await db.model('todos', {
name: String,
body: String,
priority: Number
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
await todos.set('priority', { name: 'docs' }, 40)
// -> 1
await todos.set('body', ['priority', '>', 1], 'not important ;)')
// -> 2
# getRaw
getRaw (
column: string, criteria?: Criteria<D>, defaultValue?: D[keyof D]
): Promise<D[keyof D]>
Works exactly like get()
but bypasses getters and retrieves
the raw database value.
ARGUMENTS
{string} column
: the property to retrieve- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{D[keyof D]} defaultValue
: returned if the result doesn't exist
RETURNS
Promise<D[keyof D]>
USAGE
const todos = await db.model('todos', {
name: String,
body: String,
priority: {
type: Number,
get: priority => { throw new Error('wrecked') }
}
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
await todos.getRaw('priority', { name: 'code' })
// -> 1
// with default value
await todos.getRaw('priority', { name: 'eat' }, 999)
// -> 999
// using the non-raw method fires the getter
// this causes the error above to be thrown
await todos.get('priority', { name: 'code' })
// -> Error: 'wrecked'
# setRaw
setRaw (
column: string, criteria: Criteria<D>, value: D[keyof D]
): Promise<number>
Works exactly like set()
but bypasses setters when
updating the target value.
ARGUMENTS
{string} column
: the column to update{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
{D[keyof D]} value
: the new value
RETURNS
Promise<number>
: the number of rows affected
USAGE
const todos = await db.model('todos', {
name: String,
body: {
type: String,
set: body => { throw new Error(`Sorry, updating 'priority' isn't allowed`) }
},
priority: {
type: Number,
set: priority => { throw new Error(`Sorry, updating 'body' isn't allowed`) }
}
})
await Promise.all([
todos.create({ name: 'code', body: 'create the best thing', priority: 1 }),
todos.create({ name: 'docs', body: 'crap what did I create', priority: 2 }),
todos.create({ name: 'tests', body: 'totally works', priority: 3 })
])
await todos.setRaw('priority', { name: 'docs' }, 40)
// -> 1
await todos.setRaw('body', ['priority', '>', 1], 'not important ;)')
// -> 2
// using the non-raw method fires the setters
// this causes the errors above to be thrown
await todos.set('priority', { name: 'docs' }, 50)
// -> Error: `Sorry, updating 'priority' isn't allowed`
await todos.set('body', ['priority', '>', 1], 'not important ;)')
// -> Error: `Sorry, updating 'body' isn't allowed`
# increment
increment (
column: string, criteria?: Criteria<D>, amount: number = 1
): Promise<number>
Increment a value at column
by a specified amount
, which defaults
to 1
if not provided.
ARGUMENTS
{string} column
: the target value- optional
{number} amount
- default =
1
- default =
- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
RETURNS
Promise<number>
: the number of rows affected
USAGE
// the amount defaults to 1, so these are equivalent
people.increment('age', { name: 'Bob' }, 1)
people.increment('age', { name: 'Bob' })
// happy birthday, Bob!
# decrement
decrement (
column: string,
criteria: Criteria<D>,
amount: number = 1,
allowNegative: boolean = false
): Promise<number>
Decrement a value at column
by a specified amount
, which defaults to 1
if not provided. To allow the target value to dip below 0
, pass true
as
the final argument.
ARGUMENTS
{string} column
: the target value- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{number} amount
- default =
1
- default =
- optional
{boolean} allowNegative
: unless set totrue
, the value will not be allowed to go below a value of0
- default =
false
- default =
RETURNS
Promise<number>
: the number of rows affected
USAGE
// the amount defaults to 1, so these are equivalent
people.decrement('age', { name: 'Benjamin Button' }, 1)
people.decrement('age', { name: 'Benjamin Button' })
// to allow negative values as a result of this query:
people.decrement('age', { name: 'Benjamin Button' }, 1, true)
people.decrement('age', { name: 'Benjamin Button' }, true)
# remove
remove (criteria: Criteria<D>): Promise<number>
Delete a row from the table matching criteria
. If criteria
is empty or
absent, nothing will be done. This is a safeguard against unintentionally
deleting everything in the table. Use clear()
if you want
to remove all rows.
ARGUMENTS
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
RETURNS
Promise<number>
: the number of rows affected
USAGE
users.remove({ expired: true })
# clear
clear (): Promise<number>
Removes all rows from the table.
RETURNS
Promise<number>
: the number of rows affected
# count
count (criteria?: Criteria<D>, options?: AggregateOptions): Promise<number>
Count the number of rows in the table.
ARGUMENTS
- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{AggregateOptions} options
:
property | type | default | description |
---|---|---|---|
distinct | boolean | false | Counts only unique values if true . |
group | string , string[] | - | Add a group clause to the query. |
RETURNS
Promise<number>
: the number of rows found, matching criteria
if specified
USAGE
Assuming we have this data in our people
table:
name | age |
---|---|
Bob | 18 |
Dale | 25 |
Harry | 32 |
people.count()
// -> 3
people.count(['age', '>', 21])
// -> 2
people.count({ age: 18 })
// -> 1
Now assume we've defined models people
, places
, things
, & ideas
.
If we use count()
with no arguments on the trilogy instance we can count
the number of tables in the database:
db.count()
// -> 4
# countIn
countIn (
column: string, criteria?: Criteria<D>, options?: AggregateOptions
): Promise<number>
Count the number of rows in the table, selecting on column
(meaning NULL
values are not counted).
ARGUMENTS
{string} column
: column to select on- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{AggregateOptions} options
: same ascount()
RETURNS
Promise<number>
: the number of rows found, matching criteria
if specified
USAGE
Assuming we have this data in our people
table:
name | age |
---|---|
Bob | 18 |
Dale | 25 |
Harry | 32 |
people.countIn('age')
// -> 3
# min
min (
column: string, criteria?: Criteria<D>, options?: AggregateOptions
): Promise<number>
Find the minimum value contained in the model, comparing all values
in column
that match criteria
.
ARGUMENTS
{string} column
: column to compare- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{AggregateOptions} options
:
property | type | default | description |
---|---|---|---|
group | string , string[] | - | Add a group clause to the query. |
RETURNS
Promise<number>
: the minimum number found by the query
USAGE
Given this data in the people
model:
name | age |
---|---|
Bob | 18 |
Dale | 25 |
Harry | 32 |
people.min('age')
// -> 18
# max
max (
column: string, criteria?: Criteria<D>, options?: AggregateOptions
): Promise<number>
Find the maximum value contained in the model, comparing all values
in column
that match criteria
.
ARGUMENTS
{string} column
: column to compare- optional
{Criteria<D>} criteria
: criteria used to restrict selection- Object syntax means 'where (key) is equal to (value)'
- Array syntax is one of:
- a length of 2, ie. a key / value pair (equal to)
- a length of 3, ie.
['age', '<', 65]
(allows other comparisons) - a list containing any number of the above forms
- optional
{AggregateOptions} options
:
property | type | default | description |
---|---|---|---|
group | string , string[] | - | Add a group clause to the query. |
RETURNS
Promise<number>
: the maximum number found by the query
USAGE
Given this data in the people
model:
name | age |
---|---|
Bob | 18 |
Dale | 25 |
Harry | 32 |
people.max('age')
// -> 32
# Hooks
Every Model
instance inherits this set of lifecycle hooks. In addition
hooks can be managed at the database level, in which case the signature
changes to also accept an optional model name to attach the subsciber to.
// subscribe to the `beforeCreate` hook on 'users':
users.beforeCreate(user => {})
db.beforeCreate('users', user => {})
// subscribe to the `beforeCreate` hook on all models:
db.beforeCreate(user => {})
# onQuery Hook
onQuery (fn: OnQueryCallback, options: OnQueryOptions = {}): () => boolean
The onQuery
hook is called each time a query is run on the database, and
receives the query in string form. This is useful for logging and debugging
features.
By default, subscribers to this hook do not receive any of the various internal
queries that trilogy executes behind the scenes. If you want these queries
to be passed as well, set the includeInternal
property to true
in the
options parameter of onQuery
:
people.onQuery(query => {
// query is now potentially one of the internal queries
// trilogy runs behind the scenes for managing models
}, { includeInternal: true })
ARGUMENTS
{OnQueryCallback} fn
: the function to call when queries are executed- optional
{OnQueryOptions} options
:
property | type | default | description |
---|---|---|---|
includeInternal | boolean | false | Include internal trilogy queries. |
RETURNS
() => boolean
: unsubscribe function that removes this callback
USAGE
const unsub = people.onQuery(query => console.log(query))
/* ...queries logged in the meantime... */
unsub()
// no more queries will be logged
# beforeCreate Hook
beforeCreate(fn: BeforeCreateCallback): () => boolean
Before an object is created, the beforeCreate
hook is called with the
object. This occurs before casting, so if a subscriber to this hook
modifies the incoming object those changes will be subject to casting.
It's also possible to prevent the object from being created entirely
by returning the EventCancellation
symbol from a subscriber callback.
ARGUMENTS
{BeforeCreateCallback} fn
: callback executed when the hook is triggered
RETURNS
() => boolean
: unsubscribe function that removes this callback
USAGE
const unsub = people.beforeCreate(person => {
if (doesNotMeetCondition(person)) {
// prevent this object from being created
return EventCancellation
}
person.creation_date = Date.now()
})
unsub()
# afterCreate Hook
afterCreate(fn: AfterCreateCallback): () => boolean
When an object is created, that object is returned to you and the
afterCreate
hook is called with it.
ARGUMENTS
{AfterCreateCallback} fn
: callback executed when the hook is triggered
RETURNS
() => boolean
: unsubscribe function that removes this callback
USAGE
const unsub = people.afterCreate(person => {
// log the object that was created
console.log(person)
})
// stop logging
unsub()
# beforeUpdate Hook
beforeUpdate(fn: BeforeUpdateCallback): () => boolean
Prior to an object being updated the beforeUpdate
hook is called with the
update delta, or the incoming changes to be made, as well as the criteria.
Casting occurs after this hook. A subscriber could choose to cancel the update
by returning the EventCancellation
symbol or alter the selection criteria.
ARGUMENTS
{BeforeUpdateCallback} fn
: callback executed when the hook is triggered
RETURNS
() => boolean
: unsubscribe function that removes this callback
USAGE
const unsub = people.beforeUpdate((changes, criteria) => {
if (criteria.id == null) {
// make an ID mandatory to update records
return EventCancellation
}
// add a manual timestamp to updated records
changes.updated_on = Date.now()
})
unsub()
# afterUpdate Hook
afterUpdate(fn: AfterUpdateCallback): () => boolean
Subscribers to the afterUpdate
hook receive modified objects after they
are updated.
ARGUMENTS
{AfterUpdateCallback} fn
: callback executed when the hook is triggered
RETURNS
() => boolean
: unsubscribe function that removes this callback
USAGE
const unsub = people.afterUpdate((updatedPeople) => {
updatedPeople.forEach(person => {
console.log(person.updated_on)
})
})
unsub()
# beforeRemove Hook
beforeRemove(fn: BeforeRemoveCallback): () => boolean
Before object removal, the criteria for selecting those objects is passed to
the beforeRemove
hook. Casting occurs after this hook. Subscribers can modify
the selection criteria or prevent the removal entirely by returning the
EventCancellation
symbol.
ARGUMENTS
{BeforeRemoveCallback} fn
: callback executed when the hook is triggered
RETURNS
() => boolean
: unsubscribe function that removes this callback
USAGE
const unsub = people.beforeRemove(criteria => {
if (criteria.id == null) {
// make an ID mandatory to remove records
return EventCancellation
}
// object will be removed
})
unsub()
# afterRemove Hook
afterRemove(fn: AfterRemoveCallback): () => boolean
A list of any removed objects is passed to the afterRemove
hook.
ARGUMENTS
{AfterRemoveCallback} fn
: callback executed when the hook is triggered
RETURNS
() => boolean
: unsubscribe function that removes this callback
USAGE
const unsub = users.afterRemove(users => {
users.forEach(user => {
console.log(`Removed ${user.name} from the database.`)
})
})
unsub()