@janiscommerce/model v8.8.3
model
📦 Installation
npm install @janiscommerce/modelBreaking changes ⚠️
6.0.0
- When use
changeKeysparam and cannot get any items, it will return an empty object (before returns an empty array)
Future versions 🔜
9.0.0
- ⚠️ Deprecated: Settings with
@janiscommerce/settingswill no longer be supported. (Replaced with AWS Parameter Store Since 8.8.0) - ⚠️ Deprecated: Usage of AWS Secrets Manager for credentials will no longer be supported. (Replaced with AWS Parameter Store Since 8.8.0)
🔧 Database connection settings
In order to use this package with a DB, you must to add the database connection settings, it can be set in service settings janiscommercerc.json (Core model) or in session (Client Model).
Configure database connection with databaseKey
Regardless if you use a Core or Client model you may set the databaseKey that your model will use to get the database connection settings. Default: 'default'.
class MyModel extends Model {
get databaseKey() {
return 'myDatabaseKey';
}
}👀 Either in Core or Client model the databaseKey connection settings structure is the same:
:information_source: The type property is the only one used by this package to fetch the correct DB Driver package to use.
:warning: The rest of the connection properties depends entirely by the DB Driver that you will use.
{
myDatabaseKey: {
write: {
"type": "mongodb",
"host": "http://write-host-name.org",
// ...
},
read: {
"type": "mongodb",
"host": "http://read-host-name.org",
// ...
}
}
}:information_source: Model types
There are two different model types:
:one: Set the databaseKey in your Model extended class
class MyModel extends Model {
get databaseKey() {
return 'core';
}
}:two: Model will try to find your databaseKey in database service settings
Using Settings, with settings in file /path/to/root/.janiscommercerc.json:
{
"database": {
"core": {
"write":{
"host": "http://my-host-name.org",
"type": "mysql",
// ...
}
}
}
}💉 Session injection
The session injection is useful when you have a dedicated database per client.
Using the public setter session, the session will be stored in the controller instance.
All the controllers and models getted using that controller will have the session injected.
:one: Set the databaseKey in your Model extended class
class MyModel extends Model {
get databaseKey() {
return 'myDatabaseKey';
}
}:two: Database connection configured with session injected
Your client should have the config for read (optional) and/or write (required) databases.
Example of received client:
{
"name": "Some Client",
"code": "some-client",
"databases": {
"default":{
"write": {
"type": "mongodb",
"host": "http://default-host-name.org",
// ...
}
},
"myDatabaseKey": {
"write": {
"type": "mongodb",
"host": "http://write-host-name.org",
// ...
},
"read": {
"type": "mongodb",
"host": "http://read-host-name.org",
// ...
}
}
}
}:outbox_tray: Getters
shouldCreateLogs (static getter). Returns if the model should log the write operations. Default:
true. For more details about logging, read the logging section.excludeFieldsInLog (static getter). Returns the fields that will be removed from the logs as an array of strings. For example:
['password', 'super-secret']. For more details about logging, read the logging section.statuses (class getter). Returns an
objectwith the default statuses (active/inactive)executionTime (class getter). Returns the time spent in ms on the las query.
:inbox_tray: Setters
- useReadDB
[Boolean](class setter) Set if model should use the read DB in all read/write DB operations. Default:false.
:gear: API
async getDb()
Example:
const dbDriver = await myModel.getDb();
await myModel.dbDriver.specialMethod(myModel);async isCore()
Since 8.8.0
Core Example:
const myCoreModel = new MyCoreModel();
const isCore = await myCoreModel.isCore(); // true expectedNon-Core Example:
const myClientModel = new MyClientModel();
const isCore = await myClientModel.isCore(); // false expectedasync get(params)
Parameters
paramsis an optional Object with filters, order, paginator.params.readonlyastrueif you want to use the Read Database.
Example
const items = await myModel.get({ filters: { status: 'active' } });async getById(id, [params])
Parameters
idis required. It can be one ID or an array of IDsparamsis an optional Object with filters, order, paginator, changeKeys.
Example
const items = await myModel.getById(123, { filters: { status: 'active' } });async getBy(field, id, [params])
Parameters
fieldis required. A string as a fieldvalueis required. It can be one value or an array of valuesparamsis an optional Object with filters, order, paginator.
Example
const items = await myModel.getBy(orderId, 123, { filters: { status: 'active' } });async getPaged(params, callback)
Parameters
paramsSee get() methodcallbackA function to be executed for each page. Receives three arguments: the items found, the current page and the page limit
Example
await myModel.getPaged({ filters: { status: 'active' } }, (items, page, limit) => {
// items is an array with the result from DB
});Default order
The default order when no order was received is field id using asc as order direction. Since 6.8.3
async getTotals(filters)
Parameters
filtersis an optional. Object with filters or array of filters. Since 7.1.0
Result object structure:
- pages: The total pages for the filters applied
- page: The current page
- limit: The limit applied in get
- total: The total number of items in DB for the applied filters
Example
await myModel.get({ filters: { status: 'active' } });
const totals = await myModel.getTotals();
/**
totals content:
{
pages: 3,
page: 1,
limit: 500,
total: 1450
}
*/const totals = await myModel.getTotals( { status: 'active' } );
/**
totals content:
{
pages: 3,
page: 1,
limit: 500,
total: 1450
}
*/async mapIdBy(field, fieldValues, [params])
Parameters
fieldField to filter for (String)fieldValuesList of values to filter for (Array<strings>)paramsSee get() method
Example
await myModel.mapIdBy('code', ['code-123', 'code-456'], {
order: { code: 'desc' }
});
/**
{
code-456: 'the-code-456-id',
code-123: 'the-code-123-id'
}
*/async mapIdByReferenceId(referencesIds, [params])
Parameters
referencesIdsList of References Ids (Array<strings>)paramsSee get() method
Example
await myModel.mapIdByReferenceId(['some-ref-id', 'other-ref-id', 'foo-ref-id'], {
order: { date: 'asc' },
filters: { foo: 'bar' }
});
/**
{
some-ref-id: 'some-id',
foo-ref-id: 'foo-id'
}
*/async distinct(key, params)
Parameters
paramsis an optional Object with filters.
Examples
const uniqueValues = await myModel.distinct('status');const uniqueValues = await myModel.distinct('status', {
filters: {
type: 'some-type'
}
});async insert(item)
Example
await myModel.insert({ foo: 'bar' });
const items = await myModel.get({ filters: { foo: 'bar' }});
/**
itemInserted content:
[
{
foo: 'bar'
}
//...
]
*/async save(item, setOnInsert)
Parameters
setOnInsertto add default values on Insert, optional
Example
await myModel.save({ foo: 'bar' }, { status: 'active' });
const items = await myModel.get({ filters: { foo: 'bar' }});
/**
items content:
[
{
foo: 'bar',
status: 'active'
}
//...
]
*/async update(values, filter, params)
Parameters
paramsoptional parameters to define some behavior of the query -skipAutomaticSetModifiedData: Boolean. When receive as true, the fieldsdateModifiedanduserModifiedare not updated automatically.
Example
await myModel.update({ updated: 1 }, { status: 5 });
// will set updated = 1 for the items that has status = 5async remove(item)
Example
await myModel.remove({ foo: 'bar' });
const items = await myModel.get({ filters: { foo: 'bar' }});
/**
items content:
[]
*/async multiInsert(items)
Example
await myModel.multiInsert([{ foo: 1 }, { foo: 2 }]);
const items = await myModel.get();
/**
items content:
[
{ foo: 1 },
{ foo: 2 }
]
*/async multiSave(items, setOnInsert)
Parameters
setOnInsertto add default values on Insert, optional
Example
await myModel.multiSave([{ foo: 1 }, { foo: 2, status: 'pending' }], { status: 'active' });
const items = await myModel.get();
/**
items content:
[
{ foo: 1, status: 'active' },
{ foo: 2, status: 'pending' }
]
*/async multiRemove(filter)
Example
await myModel.multiRemove({ status: 2 });
const items = await myModel.get({ filters: { status: 2 }});
/**
items content:
[]
*/async increment(filters, incrementData)
Example
await myModel.increment({ uniqueIndex: 'bar' }, { increment: 1, decrement: -1 });
/**
before:
items content:
[
{
increment: 1,
decrement: 2
}
//...
]
after:
items content:
[
{
increment: 2,
decrement: 1
}
//...
]
*/static changeKeys(items, newKey)
Parameters
itemsThe items arraynewKeyThe common field in items that will be used as key for each item
Example
const myItems = await myModel.get();
/*
[
{ some: 'item', otherProp: false },
{ some: 'otherItem', otherProp: true }
]
*/
const myItemsByKey = MyModel.changeKeys(myItems, 'some');
/*
{
item: { some: 'item', otherProp: false },
otherItem: { some: 'otherItem', otherProp: true }
}
*/:information_source: In get methods such as get and getBy you can add the changeKeys param with the newKey value.
const myItems = await myModel.get({ changeKeys: 'some' });
/*
{
item: { some: 'item', otherProp: false },
otherItem: { some: 'otherItem', otherProp: true }
}
*/:informationsource: _Since 6.0.0: When no items were found it will return an empty object
const myItems = await myModel.get({ filters: { status: 'active' }, changeKeys: 'other' });
/*
{}
*/async dropDatabase()
Example
await myModel.dropDatabase();async aggregate(stages)
:warning: Not supported by all database connectors
Parameters
stagesAn array with the aggregation stagesoptionsAn object with additional options
Examples
const results = await myModel.aggregate([
{ $match: { id: '0000000055f2255a1a8e0c54' } }, // find the document with that id
{ $unset: 'category' }, // Removes the category field
]);
/**
[
{
id: '0000000055f2255a1a8e0c54',
name: 'Product 1',
description: 'Product 1 description'
}
]
*/await myModel.aggregate([
{ $group: { _id: '$status', count: { $sum: 1 } } },
], {
allowDiskUse: true,
hint: { status: 1 }
});
/* >
{
active: 2342,
inactive: 992
}
*/async multiUpdate(operations)
Example
await myModel.multiUpdate([
{ filter: { id: [1,2] }, data: { name: 'test 1' } },
{ filter: { otherId: 3 }, data: { name: 'test 2' } }
]);
const items = await myModel.get();
/**
items content:
[
{ id: 1, name: 'test 1' },
{ id: 4, otherId: 3, name: 'test 2' }
]
*/:bookmark_tabs: Indexes Manipulation
:warning: Only if DB Driver supports it
async getIndexes()
Example
await myModel.getIndexes();
/*
[
{ name: '_id_', key: { _id_: 1}, unique: true },
{ name: 'code', key: { code: 1} }
]
*/async createIndex(index)
Example
await myModel.createIndex({ name: 'name', key: { name: 1}, unique: true });async createIndexes(indexes)
Example
await myModel.createIndexes([{ name: 'name', key: { name: 1}, unique: true }, { name: 'code', key: { code: -1 }}]);async dropIndex(name)
Example
await myModel.dropIndex('name');async dropIndexes(names)
Example
await myModel.dropIndexes(['name', 'code']);async getIdStruct()
Example (for mongodb DB)
const idStruct = await myModel.getIdStruct();
/*
struct('objectId')
*/:clipboard: Logging
This package automatically logs any write operation such as:
insert()multiInsert()update()save()multiSave()increment()remove()multiRemove()
:information_source: The logs will be added using the package @janiscommerce/log.
Automatic data in log
The package will add automatic fields in the log Object field.
executionTime. Each log will have the time spent on the query. Since 6.6.0itemsBatch. Exclusively for methodsmultiInsert()andmultiSave(), will be added the quantity of items inserted or updated in the same query. Since 6.6.0
:no_mouth: Disabling automatic logging
- For all Model operation: by setting the
static getter shouldCreateLogstofalse.
Example
class MyModel extends Model {
static get shouldCreateLogs() {
return false;
}
}- For the "next" operation: by using the method
disableLogs(). Since 8.3.0
:information_source: The logs are disabled only for the following operation
Example
// will not save logs
await myModel.disableLogs().insert({
pet: 'roger',
animal: 'dog',
age: 8
});
// will save logs normally
await myModel.insert({
pet: 'ringo',
animal: 'dog',
age: 7
});:no_entry_sign: Excluding fields from logs
Specify the fields to exclude by setting them in the static getter excludeFieldsInLog:
class MyModel extends Model {
static get excludeFieldsInLog() {
return [
'password',
'address',
'secret'
]
}
}By setting this when you do an operation with an item like:
await myModel.insert({
user: 'johndoe',
password: 'some-password',
country: 'AR',
address: 'Fake St 123'
});It will be logged as:
{
id: '5ea30bcbe28c55870324d9f0',
user: 'johndoe',
country: 'AR'
}:memo: Set custom log data
Adding a custom message:
await myModel
.setLogData('custom message!')
.insert({ foo: 'bar' });
/*
Log: {
...logData,
message: 'custom message!'
}
*/Adding a custom object data:
await myModel
.setLogData({message:'custom message!', type:'some type'})
.insert({ foo: 'bar' });
/*
Log: {
...logData,
message: 'custom message!',
type:'some type'
}
*/Adding a custom object data with log property name:
await myModel
.setLogData({message:'custom message!', type:'some type', log: { isTest: true }})
.insert({ foo: 'bar' });
/*
Log: {
...logData,
message: 'custom message!',
type:'some type',
log:{
...defaultModelLogData,
isTest: true
}
}
*/🔑 Secrets
⚠️ Deprecated: This configuration will no longer be supported starting from version 9.0.0. ⚠️
The package will get the secret using the JANIS_SERVICE_NAME environment variable.
If the secret is found, the result will be merged with the settings found in the janiscommercerc.json file or in the Client databases configuration. See Database connection settings.
The Secrets are stored in AWS Secrets Manager and obtained with the package @janiscommerce/aws-secrets-manager
- Settings in file or Client.
{
"core": {
"write": {
"type": "mongodb",
"database": "core",
"otherDBDriverConfig": 100
}
}
}- Secret fetched.
{
"databases": {
"core": {
"write": {
"host": "mongodb+srv://some-host.mongodb.net",
"user": "secure-user",
"password": "secure-password",
}
}
}
}- Config passed to the Database Driver.
{
"core": {
"write": {
"type": "mongodb",
"database": "core",
"otherDBDriverConfig": 100,
"host": "mongodb+srv://some-host.mongodb.net",
"user": "secure-user",
"password": "secure-password",
}
}
}Skip Credential Fetching
To skip the fetch of the credentials, it can be used the setting skipFetchCredentials set as true.
{
"core": {
"write": {
"skipFetchCredentials": true,
"type": "mongodb",
"protocol": "mongodb+srv://",
"host": "mongodb+srv://some-host.mongodb.net",
"user": "some-user",
"password": "insecure-password"
}
}
}10 months ago
1 year ago
8 months ago
10 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago