Реализация репозитория для работы с базами данных
|
|
2 years ago | |
|---|---|---|
| .husky | 2 years ago | |
| src | 2 years ago | |
| .c8rc | 2 years ago | |
| .commitlintrc | 2 years ago | |
| .editorconfig | 2 years ago | |
| .eslintignore | 2 years ago | |
| .eslintrc.cjs | 2 years ago | |
| .gitignore | 2 years ago | |
| .mocharc.cjs | 2 years ago | |
| .prettierrc | 2 years ago | |
| LICENSE | 2 years ago | |
| README.md | 2 years ago | |
| mocha.setup.js | 2 years ago | |
| package.json | 2 years ago | |
| tsconfig.json | 2 years ago |
Абстракция для работы с базами данных для Node.js
npm install @e22m4u/js-repository
Опционально устанавливаем адаптер. Например, если используется MongoDB, то для подключения потребуется установить адаптер mongodb как отдельную зависимость.
npm install @e22m4u/js-repository-mongodb-adapter
Список доступных адаптеров:
| адаптер | описание |
|---|---|
memory |
виртуальная база в памяти процесса (для разработки и тестирования) |
mongodb |
MongoDB - система управления NoSQL базами (требует установки) |
Модуль позволяет спроектировать систему связанных данных, доступ к которым осуществляется посредством репозиториев. Каждый репозиторий имеет собственную модель данных, которая описывает структуру определенной коллекции в базе, а так же определяет связи к другим коллекциям.
flowchart LR
A[Datasource]-->B[Data Model]-->С[Repository];
Определения источников и моделей хранятся в экземпляре класса
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', // выбранный адаптер
});
Когда источники определены, можно перейти к описанию моделей данных. Модель может определять как структуру какого-либо объекта, так и являться отражением реальной коллекции базы.
Представьте себе коллекцию торговых точек, у каждой из которых
имеются координаты lat и lng. Мы можем заранее
определить модель для объекта координат методом defineModel
и использовать ее в других коллекциях.
schema.defineModel({
name: 'latLng', // название новой модели
properties: { // поля модели
lat: DataType.NUMBER, // поле широты
lng: DataType.NUMBER, // поле долготы
},
});
Параметры модели:
name: string уникальное название (обязательно)datasource: string выбранный источник данныхproperties: object определения полей моделиrelations: object определения связанных коллекцийПараметр properties принимает объект, ключи которого
являются именами полей, а значением тип поля или объект с
дополнительными параметрами.
schema.defineModel({
name: 'latLng',
properties: {
lat: DataType.NUMBER, // краткое определение поля "lat"
lng: { // расширенное определение поля "lng"
type: DataType.NUMBER, // тип допустимого значения
required: true, // исключает null и undefined
},
},
});
Типы данных:
DataType.ANYDataType.STRINGDataType.NUMBERDataType.BOOLEANDataType.ARRAYDataType.OBJECTМодель latLng всего лишь описывает структуру объекта
координат, тогда как торговая точка должна иметь реальную таблицу в
базе. По аналогии с предыдущим примером, добавим модель
place, но дополнительно укажем источник данных в параметре
datasource
schema.defineModel({
name: 'place',
datasource: 'myMemory', // выбранный источник данных
properties: {
name: DataType.STRING, // поле для названия торговой точки
location: { // поле для объекта координат
type: DataType.OBJECT, // допускать только объекты
model: 'latLng', // определение модели объекта координат
},
},
});
В примере выше мы использовали модель latLng как
структуру допустимого значения поля location. Возможный
документ данной коллекции может выглядеть так:
{
"id": 1,
"name": "Burger King",
"location": {
"lat": 32.412891,
"lng": 34.7660061
}
}
Стоит обратить внимание, что мы могли бы не объявлять параметр
properties, при этом теряя возможность проверки данных
перед записью в базу.
schema.defineModel({
name: 'place',
adapter: 'myMemory',
// параметр "properties" не указан
});
Параметры поля:
type: string тип допустимого значения
(обязательно)itemType: string тип элемента массива (для
type: 'array')model: string модель объекта (для
type: 'object')primaryKey: boolean объявить поле первичным ключомcolumnName: string переопределение названия
колонкиcolumnType: string тип колонки (определяется
адаптером)required: boolean объявить поле обязательнымdefault: any значение по умолчаниюВ отличие от latLng, модель place имеет
источник данных с названием myMemory, который был объявлен
ранее. Наличие источника позволяет получить репозиторий по названию
модели.
const rep = schema.getRepository('place');
Методы:
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
Рассмотрим применение параметра 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'}} флаги регулярного
выражения
npm run test
MIT