Реализация репозитория для работы с базами данных
|
|
2 лет назад | |
|---|---|---|
| .husky | 2 лет назад | |
| src | 2 лет назад | |
| .c8rc | 2 лет назад | |
| .commitlintrc | 2 лет назад | |
| .editorconfig | 2 лет назад | |
| .eslintignore | 2 лет назад | |
| .eslintrc.cjs | 2 лет назад | |
| .gitignore | 2 лет назад | |
| .mocharc.cjs | 2 лет назад | |
| .prettierrc | 2 лет назад | |
| LICENSE | 2 лет назад | |
| README.md | 2 лет назад | |
| mocha.setup.js | 2 лет назад | |
| package.json | 2 лет назад | |
| tsconfig.json | 2 лет назад |
Абстракция для работы с базами данных для Node.js
npm install @e22m4u/js-repository
Опционально устанавливаем адаптер. Например, если используется MongoDB, то для подключения потребуется установить адаптер mongodb как отдельную зависимость.
npm install @e22m4u/js-repository-mongodb-adapter
Доступные адаптеры
| описание | |
|---|---|
memory |
виртуальная база в памяти процесса (для разработки и тестирования) |
mongodb |
MongoDB - система управления NoSQL базами (установка) |
Модуль позволяет спроектировать систему связанных данных, доступ к которым осуществляется посредством репозиториев. Каждый репозиторий имеет собственную модель, которая описывает структуру документа коллекции, а так же определяет связи к другим моделям.
flowchart LR
A[Источник]-->B[Модель]-->С[Ропозиторий];
Перед началом работы с коллекциями потребуется выполнить настройку
проекта, которая заключается в определении источников и моделей данных в
экземпляре класса Schema. И первым шагом будет создание
данного экземпляра.
import {Schema} from '@e22m4u/js-repository';
const schema = new Schema();
Интерфейс Schema содержит три основных метода:
defineDatasource(datasourceDef: object): this -
добавить источникdefineModel(modelDef: object): this - добавить
модельgetRepository(modelName: string): Repository - получить
репозиторийИсточник описывает способ подключения к базе и используемый адаптер.
Если адаптер имеет настройки, то они передаются вместе с объектом
определения источника методом defineDatasource, как это
показано ниже.
schema.defineDatasource({
name: 'myMongo', // название нового источника
adapter: 'mongodb', // название выбранного адаптера
// настройки адаптера mongodb
host: '127.0.0.1',
port: 27017,
database: 'data'
});
Параметры источника
name: string уникальное названиеadapter: string выбранный адаптерПри желании можно использовать встроенный адаптер
memory, который хранит данные в памяти процесса. У него нет
специальных настроек, и он отлично подходит для тестов и
прототипирования.
schema.defineDatasource({
name: 'myMemory', // название источника
adapter: 'memory', // выбранный адаптер
});
Модель данных описывает структуру документа и может указывать на источник, который будет использован репозиторием для доступа к коллекции.
// объявление модели "city"
schema.defineModel({
name: 'city', // название новой модели
properties: { // поля модели
name: DataType.STRING,
population: DataType.NUMBER,
},
});
Название модели должно быть уникальным, так как оно используется для
определения отношений к другим моделям, и для названия таблицы в базе
данных. При необходимости можно явно задать название таблицы параметром
tableName, если оно не соответствует названию модели.
Если модель отражает реальную коллекцию базы, а не является частью
другой, то указывая параметр datasource появляется
возможность получить репозиторий этой модели (см. Репозиторий), с помощью которого выполняются
операции чтения и записи в базу.
schema.defineModel({
name: 'city',
datasource: 'myMemory', // выбранный источник
properties: {
name: DataType.STRING,
population: DataType.NUMBER,
},
});
Параметр properties принимает объект, ключи которого
являются именами полей, а значением тип поля или объект с
дополнительными параметрами. Эти настройки используются для проверки
данных перед сохранением в базу и установки значений по умолчанию (если
имеются).
schema.defineModel({
name: 'city',
datasource: 'myMemory',
properties: {
name: DataType.STRING,
population: { // расширенные параметры
type: DataType.NUMBER, // тип значения
default: 0, // значение по умолчанию
},
},
});
name: string название модели (обязательно)tableName: string название коллекции в базеdatasource: string выбранный источник данныхproperties: object определения полей моделиrelations: object определения связей (см. Связи)DataType.ANY разрешено любое значениеDataType.STRING только значение типа
stringDataType.NUMBER только значение типа
numberDataType.BOOLEAN только значение типа
booleanDataType.ARRAY только значение типа
arrayDataType.OBJECT только значение типа
objecttype: string тип допустимого значения
(обязательно)itemType: string тип элемента массива (для
type: 'array')model: string модель объекта (для
type: 'object')primaryKey: boolean объявить поле первичным ключомcolumnName: string переопределение названия
колонкиcolumnType: string тип колонки (определяется
адаптером)required: boolean объявить поле обязательнымdefault: any значение по умолчаниюОбъявление модели имеет необязательный параметр
datasource, который указывает на источник хранения данных.
Наличие источника позволяет получить репозиторий по названию такой
модели. Репозиторий, в свою очередь, предоставляет интерфейс для чтения
и записи данных этой модели.
import {Schema} from '@e22m4u/js-repository';
const schema = new Schema();
// объявление источника "myMemory"
schema.defineDatasource({
name: 'myMemory',
adapter: 'memory',
});
// объявление модели "city"
schema.defineModel({
name: 'city',
datasource: 'myMemory', // источник данных
properties: {
name: DataType.STRING,
population: DataType.NUMBER,
},
});
// получение репозитория модели "city"
const cityRep = schema.getRepository('city');
// записываем новый документ "city"
const city = await cityRep.create({
name: 'Moscow',
population: 11980000
});
// выводим результат
console.log(city);
// {
// "id": 1,
// "name": "Moscow",
// "population": 11980000
// }
create(data, filter = undefined) добавить новый
документreplaceById(id, data, filter = undefined) заменить весь
документpatchById(id, data, filter = undefined) частично
обновить документpatch(data, where = undefined) обновить все документы
или по условиюfind(filter = undefined) найти все документы или по
условиюfindOne(filter = undefined) найти первый документ или
по условиюfindById(id, filter = undefined) найти документ по
идентификаторуdelete(where = undefined) удалить все документы или по
условиюdeleteById(id) удалить документ по идентификаторуexists(id) проверить существование по
идентификаторуcount(where = undefined) подсчет всех документов или по
условиюid идентификатор (первичный ключ)data объект отражающий состав документаwhere параметры выборки (см. Фильтрация)filter параметры возвращаемого результата (см.
Фильтрация)Некоторые методы репозитория опционально принимают параметр
where для условий выборки и/или параметр
filter управляющий возвращаемым значением.
Рассмотрим применение параметра where на примере метода
patch, который выполняет поиск и обновление документов по
определенному условию.
Сигнатура метода patch(data, where = undefined)
указывает на два параметра, где первый принимает объект данных, а второй
является опциональным объектом выборки.
// вызов метода `patch`
await rep.patch(
// обновить значения полей
// "hidden" и "updatedAt"
{
hidden: true,
updatedAt: new Date().toISOString(),
},
// только в тех документах, где поле "type"
// равно значению "article", а "publishedAt"
// содержит дату более раннюю, чем указана
// в операторе "lte"
{
type: 'article',
publishedAt: {
lte: '2023-12-02T14:00:00.000Z',
}
},
);
Второй аргумент метода patch предыдущего листинга
содержит условия выборки по которым будет выполнен поиск и обновление
документов. Полный список возможных операторов сравнения приводится
ниже.
{foo: 'bar'} поиск по значению поля
foo
{foo: {eq: 'bar'}} оператор равенства eq
{foo: {neq: 'bar'}} оператор неравенства
neq
{foo: {gt: 5}} оператор "больше" gt
{foo: {lt: 10}} оператор "меньше" lt
{foo: {gte: 5}} оператор "больше или равно"
gte
{foo: {lte: 10}} оператор "меньше или равно"
lte
{foo: {inq: ['bar', 'baz']}} равенство одного из значений
inq
{foo: {nin: ['bar', 'baz']}} исключение значений массива
nin
{foo: {between: [5, 10]}} оператор диапазона
between
{foo: {exists: true}} оператор наличия значения
exists
{foo: {like: 'bar'}} оператор поиска подстроки
like
{foo: {ilike: 'BaR'}} регистронезависимая версия
ilike
{foo: {nlike: 'bar'}} оператор исключения подстроки
nlike
{foo: {nilike: 'BaR'}} регистронезависимая версия
nilike
{foo: {regexp: 'ba.+'}} оператор регулярного выражения
regexp
{foo: {regexp: 'ba.+', flags: 'i'}} флаги регулярного
выражения
Методы репозитория асинхронны и возвращают Promise с
некоторым значением. Параметр filter позволяет влиять на
это значение более широким образом, чем просто ограничение для
затрагиваемых данных. Ниже представлен список опциональных полей объекта
filter, одним из которых является where
(рассматривался ранее).
where объект выборкиorder указание порядкаlimit ограничение количества документовskip пропуск документовfields выбор необходимых полей документаinclude включение связанных данных в результат (см.
Связи)Документы могут быть отсортированы по указанным полям и в нужном
направлении, где ASC - по умолчанию, и DESC -
в обратном порядке.
// на примере метода "find"
const result = await rep.find({
// упорядочить по полю "featured"
order: 'featured',
// по полю "featured" в обратном порядке
order: 'featured DESC',
// по нескольким полям в разных направлениях
order: ['featured ASC', 'publishedAt DESC', 'id'],
});
При использовании метода find может потребоваться
комбинация параметров limit и skip для
механизма пагинации.
// первый параметр метода `find` принимает
// объект настроек возвращаемого результата
const result = await rep.find({
limit: 12, // вернуть не более 14и документов
skip: 24, // пропустить 24 документа выборки
});
Сократить объем документа можно параметром fields указав
необходимый набор полей.
// на примере метода "find"
const result = await rep.find({
// включить в результат только поле "title"
fields: 'title',
// или поле "title", "createdAt" и "featured"
fields: ['title', 'createdAt', 'featured'],
// первичный ключ указывать не обязательно
});
WIP
npm run test
MIT