|
@@ -1,1706 +0,0 @@
|
|
|
-## @e22m4u/js-repository
|
|
|
|
|
-
|
|
|
|
|
-Абстракция для работы с базами данных для Node.js
|
|
|
|
|
-
|
|
|
|
|
-<!--
|
|
|
|
|
-
|
|
|
|
|
-- [Установка](#Установка)
|
|
|
|
|
-- [Введение](#Введение)
|
|
|
|
|
-- [Пример](#Пример)
|
|
|
|
|
-- [Источник данных](#Источник-данных)
|
|
|
|
|
-- [Модель данных](#Модель-данных)
|
|
|
|
|
-- [Репозиторий](#Репозиторий)
|
|
|
|
|
-- [Фильтрация](#Фильтрация)
|
|
|
|
|
-- [Связи](#Связи)
|
|
|
|
|
-- [Тесты](#Тесты)
|
|
|
|
|
-
|
|
|
|
|
-## Установка
|
|
|
|
|
-
|
|
|
|
|
-```bash
|
|
|
|
|
-npm install @e22m4u/js-repository
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Опционально устанавливаем адаптер. Например, если используется
|
|
|
|
|
-*MongoDB*, то для подключения потребуется установить
|
|
|
|
|
-[адаптер mongodb](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter)
|
|
|
|
|
-как отдельную зависимость.
|
|
|
|
|
-
|
|
|
|
|
-```bash
|
|
|
|
|
-npm install @e22m4u/js-repository-mongodb-adapter
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Список адаптеров:
|
|
|
|
|
-
|
|
|
|
|
-| | описание |
|
|
|
|
|
-|-----------|--------------------------------------------------------------------------------------------------------------------------------|
|
|
|
|
|
-| `memory` | виртуальная база в памяти процесса (не требует установки) |
|
|
|
|
|
-| `mongodb` | MongoDB - система управления NoSQL базами (*[установка](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
|
|
|
|
|
-
|
|
|
|
|
-## Введение
|
|
|
|
|
-
|
|
|
|
|
-Модуль позволяет объединить несколько источников данных в единую систему данных с помощью источников
|
|
|
|
|
-и моделей. Источник описывает способ подключения к базе и место хранения данных,
|
|
|
|
|
-а модель структуру документа и связи между коллекциями. Непосредственно чтение
|
|
|
|
|
-и запись осуществляется репозиториями, которые можно получить после объявления
|
|
|
|
|
-моделей.
|
|
|
|
|
-
|
|
|
|
|
-```mermaid
|
|
|
|
|
-flowchart LR
|
|
|
|
|
-
|
|
|
|
|
-A[Источник данных]-->B[Модель A]-->D[Репозиторий А]
|
|
|
|
|
-A[Источник данных]-->C[Модель Б]-->E[Репозиторий Б]
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Пример
|
|
|
|
|
-
|
|
|
|
|
-Перед началом работы с коллекцией нужно определить источник и модель данных.
|
|
|
|
|
-Определения хранятся в экземпляре класса `Schema`, и первым шагом будет
|
|
|
|
|
-создание данного экземпляра.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-import {Schema} from '@e22m4u/js-repository';
|
|
|
|
|
-
|
|
|
|
|
-const schema = new Schema();
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Интерфейс `Schema` имеет три основных метода:
|
|
|
|
|
-
|
|
|
|
|
-- `defineDatasource(datasourceDef: object): this` - добавить источник
|
|
|
|
|
-- `defineModel(modelDef: object): this` - добавить модель
|
|
|
|
|
-- `getRepository(modelName: string): Repository` - получить репозиторий
|
|
|
|
|
-
|
|
|
|
|
-Далее определяется источник и выбранный адаптер. Количество источников
|
|
|
|
|
-зависит от числа используемых баз данных.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineDatasource({
|
|
|
|
|
- name: 'myMemory', // название нового источника
|
|
|
|
|
- adapter: 'memory', // выбранный адаптер
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Теперь добавим модель отражающую структуру документа. Представим
|
|
|
|
|
-коллекцию `city` с двумя полями, где строковое поле `name` в качестве
|
|
|
|
|
-названия города, и `population` как численность населения.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'city', // название модели
|
|
|
|
|
- datasource: 'myMemory', // выбранный источник
|
|
|
|
|
- properties: {
|
|
|
|
|
- name: DataType.STRING, // поле типа "string"
|
|
|
|
|
- population: DataType.NUMBER, // поле типа "number"
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Модель гарантирует, что тип указанных полей будет проверен перед записью
|
|
|
|
|
-в коллекцию. Осталось получить репозиторий данной модели для чтения и записи
|
|
|
|
|
-документов.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// получаем репозиторий модели "city"
|
|
|
|
|
-const cityRep = schema.getRepository('city');
|
|
|
|
|
-
|
|
|
|
|
-// добавляем новый документ
|
|
|
|
|
-const city = await cityRep.create({
|
|
|
|
|
- name: 'Moscow',
|
|
|
|
|
- population: 11980000,
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// выводим результат
|
|
|
|
|
-console.log(city);
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "name": "Moscow",
|
|
|
|
|
-// "population": 11980000
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Последующие обращения к методу `getRepository` с названием модели
|
|
|
|
|
-`city` будут возвращать все тот же экземпляр репозитория данной
|
|
|
|
|
-модели по принципу «одиночки».
|
|
|
|
|
-
|
|
|
|
|
-## Источник данных
|
|
|
|
|
-
|
|
|
|
|
-Каждый источник имеет свое уникальное название и выбранный адаптер.
|
|
|
|
|
-Экземпляр класса `Schema` имеет метод `defineDatasource` с помощью
|
|
|
|
|
-которого объявляется новый источник.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-import {Schema} from '@e22m4u/js-repository';
|
|
|
|
|
-
|
|
|
|
|
-const schema = new Schema();
|
|
|
|
|
-
|
|
|
|
|
-schema.defineDatasource({
|
|
|
|
|
- name: 'datasource1', // название нового источника
|
|
|
|
|
- adapter: 'memory', // выбранный адаптер
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Если адаптер имеет параметры, то они передаются с объектом определения
|
|
|
|
|
-источника.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineDatasource({
|
|
|
|
|
- name: 'datasource2',
|
|
|
|
|
- adapter: 'mongodb',
|
|
|
|
|
- // настройки адаптера "mongodb"
|
|
|
|
|
- host: '127.0.0.1',
|
|
|
|
|
- port: 27017,
|
|
|
|
|
- database: 'data'
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Параметры источника:
|
|
|
|
|
-
|
|
|
|
|
-- `name: string` уникальное название
|
|
|
|
|
-- `adapter: string` выбранный адаптер
|
|
|
|
|
-- параметры адаптера (если имеются)
|
|
|
|
|
-
|
|
|
|
|
-**Адаптер «memory»**
|
|
|
|
|
-
|
|
|
|
|
-Встроенный адаптер `memory` хранит данные в памяти процесса, не требует
|
|
|
|
|
-установки и отлично подходит для тестов и прототипирования.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-import {Schema} from '@e22m4u/js-repository';
|
|
|
|
|
-
|
|
|
|
|
-const schema = new Schema();
|
|
|
|
|
-
|
|
|
|
|
-schema.defineDatasource({
|
|
|
|
|
- name: 'myMemory',
|
|
|
|
|
- adapter: 'memory', // адаптер "memory"
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'fruit',
|
|
|
|
|
- datasource: 'myMemory',
|
|
|
|
|
- properties: {
|
|
|
|
|
- name: DataType.STRING,
|
|
|
|
|
- description: DataType.STRING,
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const fruitRep = schema.getRepository('fruit');
|
|
|
|
|
-
|
|
|
|
|
-const pineapple = fruitRep.create({
|
|
|
|
|
- name: 'Pineapple',
|
|
|
|
|
- description: 'A tropical fruit'
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-console.log(pineapple);
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "name": "Pineapple",
|
|
|
|
|
-// "description": "A tropical fruit"
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Адаптер `memory` использует автоинкрементные идентификаторы типа `number`,
|
|
|
|
|
-но может принимать и строковые ключи, если их значение явно указано
|
|
|
|
|
-в документе.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// ...
|
|
|
|
|
-
|
|
|
|
|
-const banana = fruitRep.create({
|
|
|
|
|
- id: 'fruit-1', // ключ типа "string"
|
|
|
|
|
- name: 'Banana',
|
|
|
|
|
- description: 'An edible fruit'
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-console.log(banana);
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": "fruit-1",
|
|
|
|
|
-// "name": 'Banana',
|
|
|
|
|
-// "description": "An edible fruit"
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Модель данных
|
|
|
|
|
-
|
|
|
|
|
-Модель описывает структуру документа определенной коллекции. Экземпляр
|
|
|
|
|
-класса `Schema` имеет метод `defineModel` с помощью которого объявляется
|
|
|
|
|
-новая модель.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-import {Schema} from '@e22m4u/js-repository';
|
|
|
|
|
-
|
|
|
|
|
-const schema = new Schema();
|
|
|
|
|
-
|
|
|
|
|
-// объявление модели "region"
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'region', // название модели
|
|
|
|
|
- properties: { // поля модели
|
|
|
|
|
- name: DataType.STRING,
|
|
|
|
|
- population: DataType.NUMBER,
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// документ "region" может выглядеть так
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "name": "Asia",
|
|
|
|
|
-// "population": 4753079727
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Параметры модели:
|
|
|
|
|
-
|
|
|
|
|
-- `name: string` название модели (обязательно)
|
|
|
|
|
-- `base: string` название наследуемой модели
|
|
|
|
|
-- `tableName: string` название коллекции в базе
|
|
|
|
|
-- `datasource: string` выбранный источник данных
|
|
|
|
|
-- `properties: object` определения полей модели
|
|
|
|
|
-- `relations: object` определения связей (см. [Связи](#Связи))
|
|
|
|
|
-
|
|
|
|
|
-**Название модели**
|
|
|
|
|
-
|
|
|
|
|
-Название должно быть уникальным, так как оно используется для
|
|
|
|
|
-определения связей и соответствует названию коллекции в базе.
|
|
|
|
|
-При необходимости можно явно задать название коллекции параметром
|
|
|
|
|
-`tableName`, если оно не соответствует названию модели.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'city', // название модели
|
|
|
|
|
- tableName: 'CITIES', // название коллекции
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Источник данных**
|
|
|
|
|
-
|
|
|
|
|
-Если модель отражает реальную коллекцию базы, а не является частью другой,
|
|
|
|
|
-то указав название источника в параметре `datasource` можно получить
|
|
|
|
|
-репозиторий модели методом `getRepository` по ее названию.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// модель "city"
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'city',
|
|
|
|
|
- datasource: 'myDatasource', // выбранный источник
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// репозиторий модели "city"
|
|
|
|
|
-const cityRep = schema.getRepository('city');
|
|
|
|
|
-// await cityRep.create({name: 'St. Petersburg'});
|
|
|
|
|
-// ...
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Параметры поля:
|
|
|
|
|
-
|
|
|
|
|
-- `type: string` тип допустимого значения (обязательно)
|
|
|
|
|
-- `itemType: string` тип элемента массива (для `type: 'array'`)
|
|
|
|
|
-- `model: string` модель объекта (для `type: 'object'`)
|
|
|
|
|
-- `primaryKey: boolean` объявить поле первичным ключом
|
|
|
|
|
-- `columnName: string` переопределение названия колонки
|
|
|
|
|
-- `columnType: string` тип колонки (определяется адаптером)
|
|
|
|
|
-- `required: boolean` объявить поле обязательным
|
|
|
|
|
-- `default: any` значение по умолчанию
|
|
|
|
|
-
|
|
|
|
|
-Типы данных:
|
|
|
|
|
-
|
|
|
|
|
-- `DataType.ANY` разрешено любое значение
|
|
|
|
|
-- `DataType.STRING` только значение типа `string`
|
|
|
|
|
-- `DataType.NUMBER` только значение типа `number`
|
|
|
|
|
-- `DataType.BOOLEAN` только значение типа `boolean`
|
|
|
|
|
-- `DataType.ARRAY` только значение типа `array`
|
|
|
|
|
-- `DataType.OBJECT` только значение типа `object`
|
|
|
|
|
-
|
|
|
|
|
-**Поля документа**
|
|
|
|
|
-
|
|
|
|
|
-Параметр `properties` принимает объект, ключи которого являются именами
|
|
|
|
|
-полей, а значением тип поля или объект с дополнительными параметрами.
|
|
|
|
|
-Эти настройки используются для проверки данных перед сохранением в базу
|
|
|
|
|
-и установки значений по умолчанию (если имеются).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'city',
|
|
|
|
|
- properties: {
|
|
|
|
|
- name: DataType.STRING, // поле "name" типа "string"
|
|
|
|
|
- population: { // поле "population" типа "number"
|
|
|
|
|
- type: DataType.NUMBER,
|
|
|
|
|
- default: 0, // значение по умолчанию
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Когда требуется указать значение по умолчанию, установить флаг `required`
|
|
|
|
|
-или объявить более сложный тип допустимого значения, то вместо базового
|
|
|
|
|
-определения используется объект с расширенными настройками.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'place',
|
|
|
|
|
- properties: {
|
|
|
|
|
- // базовое определение поля "name"
|
|
|
|
|
- name: DataType.STRING,
|
|
|
|
|
- // расширенное определение поля "location"
|
|
|
|
|
- // с дополнительными параметрами "model"
|
|
|
|
|
- // и "required"
|
|
|
|
|
- location: {
|
|
|
|
|
- type: DataType.OBJECT,
|
|
|
|
|
- model: 'latLng',
|
|
|
|
|
- required: true,
|
|
|
|
|
- },
|
|
|
|
|
- // расширенное определение поля "keywords"
|
|
|
|
|
- // с дополнительными параметрами "itemType"
|
|
|
|
|
- // и "default"
|
|
|
|
|
- keywords: {
|
|
|
|
|
- type: DataType.ARRAY,
|
|
|
|
|
- itemType: DataType.STRING,
|
|
|
|
|
- default: () => [],
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Наследование**
|
|
|
|
|
-
|
|
|
|
|
-Модель может наследовать поля и связи используя параметр `base`, куда
|
|
|
|
|
-передается название базовой модели. При этом наследуемые настройки можно
|
|
|
|
|
-переопределять не затрагивая родителя.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// модель "area"
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'area',
|
|
|
|
|
- properties: {
|
|
|
|
|
- name: DataType.STRING,
|
|
|
|
|
- population: DataType.NUMBER,
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// модель "city"
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- name: 'city',
|
|
|
|
|
- base: 'area', // базовая модель
|
|
|
|
|
- properties: {
|
|
|
|
|
- timezone: DataType.STRING,
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// документ "city" может выглядеть так
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "name": "Moscow", <= унаследовано
|
|
|
|
|
-// "population": 11980000, <= унаследовано
|
|
|
|
|
-// "timezone": "Europe/Moscow"
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Репозиторий
|
|
|
|
|
-
|
|
|
|
|
-Объявление модели имеет необязательный параметр `datasource`,
|
|
|
|
|
-который указывает на источник хранения данных. Наличие источника
|
|
|
|
|
-позволяет получить репозиторий по названию такой модели. Репозиторий,
|
|
|
|
|
-в свою очередь, предоставляет интерфейс для чтения и записи данных
|
|
|
|
|
-этой модели.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-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
|
|
|
|
|
-
|
|
|
|
|
-Рассмотрим применение параметра `where` на примере метода `patch`, который
|
|
|
|
|
-выполняет поиск и обновление документов по определенному условию.
|
|
|
|
|
-
|
|
|
|
|
-Сигнатура метода `patch(data, where = undefined)` указывает на два параметра,
|
|
|
|
|
-где первый принимает объект данных, а второй является опциональным объектом
|
|
|
|
|
-выборки.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// вызов метода `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'}}` флаги регулярного выражения
|
|
|
|
|
-
|
|
|
|
|
-### filter
|
|
|
|
|
-
|
|
|
|
|
-Методы репозитория асинхронны и возвращают `Promise` с некоторым значением.
|
|
|
|
|
-Параметр `filter` позволяет влиять на это значение более широким образом,
|
|
|
|
|
-чем просто ограничение для затрагиваемых данных. Ниже представлен список
|
|
|
|
|
-опциональных полей объекта `filter`, одним из которых является `where`
|
|
|
|
|
-(рассматривался ранее).
|
|
|
|
|
-
|
|
|
|
|
-- `where` объект выборки
|
|
|
|
|
-- `order` указание порядка
|
|
|
|
|
-- `limit` ограничение количества документов
|
|
|
|
|
-- `skip` пропуск документов
|
|
|
|
|
-- `fields` выбор необходимых полей документа
|
|
|
|
|
-- `include` включение связанных данных в результат (см. Связи)
|
|
|
|
|
-
|
|
|
|
|
-#### order
|
|
|
|
|
-
|
|
|
|
|
-Документы могут быть отсортированы по указанным полям и в нужном
|
|
|
|
|
-направлении, где `ASC` - по умолчанию, и `DESC` - в обратном порядке.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// на примере метода "find"
|
|
|
|
|
-const result = await rep.find({
|
|
|
|
|
- // упорядочить по полю "featured"
|
|
|
|
|
- order: 'featured',
|
|
|
|
|
- // по полю "featured" в обратном порядке
|
|
|
|
|
- order: 'featured DESC',
|
|
|
|
|
- // по нескольким полям в разных направлениях
|
|
|
|
|
- order: ['featured ASC', 'publishedAt DESC', 'id'],
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### limit и skip
|
|
|
|
|
-
|
|
|
|
|
-При использовании метода `find` может потребоваться комбинация параметров
|
|
|
|
|
-`limit` и `skip` для механизма пагинации.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// первый параметр метода `find` принимает
|
|
|
|
|
-// объект настроек возвращаемого результата
|
|
|
|
|
-const result = await rep.find({
|
|
|
|
|
- limit: 12, // вернуть не более 14и документов
|
|
|
|
|
- skip: 24, // пропустить 24 документа выборки
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### fields
|
|
|
|
|
-
|
|
|
|
|
-Сократить объем документа можно параметром `fields` указав необходимый
|
|
|
|
|
-набор полей.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// на примере метода "find"
|
|
|
|
|
-const result = await rep.find({
|
|
|
|
|
- // включить в результат только поле "title"
|
|
|
|
|
- fields: 'title',
|
|
|
|
|
- // или поле "title", "createdAt" и "featured"
|
|
|
|
|
- fields: ['title', 'createdAt', 'featured'],
|
|
|
|
|
- // первичный ключ указывать не обязательно
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Связи
|
|
|
|
|
-
|
|
|
|
|
-WIP
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция имеет три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "type": "city",
|
|
|
|
|
-// "name": "Bangkok",
|
|
|
|
|
-// "hidden": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "type": "country",
|
|
|
|
|
-// "name": "Thailand",
|
|
|
|
|
-// "hidden": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "type": "city",
|
|
|
|
|
-// "name": "Moscow",
|
|
|
|
|
-// "hidden": true
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `patch` с передачей
|
|
|
|
|
-// значений для обновляемых полей
|
|
|
|
|
-const result = await rep.patch({
|
|
|
|
|
- hidden: false,
|
|
|
|
|
- updatedAt: new Date().toISOString(),
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// вывод количество затронутых документов
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// 3
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### create(data, filter = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Добавляет новый документ в коллекцию и возвращает его.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-const result = await rep.create({
|
|
|
|
|
- name: 'Rick Sanchez',
|
|
|
|
|
- dimension: 'C-137',
|
|
|
|
|
- age: 67,
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// вывод результата
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// id: 1, <= определено базой данных
|
|
|
|
|
-// name: 'Rick Sanchez',
|
|
|
|
|
-// dimension: 'C-137',
|
|
|
|
|
-// age: 67
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Ограничение количества возвращаемых полей.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-const result = await rep.create(
|
|
|
|
|
- // состав нового документа
|
|
|
|
|
- {
|
|
|
|
|
- name: 'Rick Sanchez',
|
|
|
|
|
- dimension: 'C-137',
|
|
|
|
|
- age: 67,
|
|
|
|
|
- },
|
|
|
|
|
- // параметры возвращаемого результата
|
|
|
|
|
- {
|
|
|
|
|
- fields: [
|
|
|
|
|
- 'name',
|
|
|
|
|
- 'age',
|
|
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
-);
|
|
|
|
|
-
|
|
|
|
|
-// вывод результата
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// id: 1,
|
|
|
|
|
-// name: 'Rick Sanchez',
|
|
|
|
|
-// age: 67
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Использование второго параметра `filter` метода `create` позволяет
|
|
|
|
|
-ограничивать количество возвращаемых данных, и разрешать связи к другим
|
|
|
|
|
-коллекциям (см. Связи).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// подготовка состава для нового документа
|
|
|
|
|
-const data = {
|
|
|
|
|
- name: 'Rick Sanchez',
|
|
|
|
|
- dimension: 'C-137',
|
|
|
|
|
- age: 67,
|
|
|
|
|
- biographyId: 59, // связь "biography" (см. belongsTo)
|
|
|
|
|
- pictureIds: [345, 346], // связь "pictures" (см. referencesMany)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// подготовка параметра "filter"
|
|
|
|
|
-const filter = {
|
|
|
|
|
- // "fields" - если определено, то результат
|
|
|
|
|
- // будут включать только указанные поля
|
|
|
|
|
- fields: [
|
|
|
|
|
- 'name',
|
|
|
|
|
- 'biographyId'
|
|
|
|
|
- 'pictureIds',
|
|
|
|
|
- ],
|
|
|
|
|
- // "include" - включение в результат
|
|
|
|
|
- // связанных документов (см. Связи)
|
|
|
|
|
- include: [
|
|
|
|
|
- 'biography',
|
|
|
|
|
- 'pictures',
|
|
|
|
|
- ],
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `create` и вывод результата
|
|
|
|
|
-const result = await rep.create(data, filter);
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// "name": "Rick Sanchez",
|
|
|
|
|
-// "biographyId": 59,
|
|
|
|
|
-// "biography": { <= разрешение связи
|
|
|
|
|
-// "id": 59,
|
|
|
|
|
-// "annotation": "This article is about Rick Sanchez",
|
|
|
|
|
-// "body": "He is a genius scientist whose ..."
|
|
|
|
|
-// }
|
|
|
|
|
-// "pictureIds": [345, 346],
|
|
|
|
|
-// "pictures": [ <= разрешение связи
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 345,
|
|
|
|
|
-// "mime": "image/jpeg",
|
|
|
|
|
-// "file": "/uploads/rick345.jpg"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 346,
|
|
|
|
|
-// "mime": "image/jpeg",
|
|
|
|
|
-// "file": "/uploads/rick346.jpg"
|
|
|
|
|
-// }
|
|
|
|
|
-// ],
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
-// поля "age" и "dimension" исключены
|
|
|
|
|
-// из результата опцией "fields"
|
|
|
|
|
-//
|
|
|
|
|
-// документы "biography" и "pictures"
|
|
|
|
|
-// встроены в результат опцией "include"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### replaceById(id, data, filter = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Заменяет существующий документ по идентификатору и возвращает его. Если
|
|
|
|
|
-идентификатор не найден, то выбрасывает исключение.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// документ с идентификатором 12
|
|
|
|
|
-// имеет следующую структуру
|
|
|
|
|
-// {
|
|
|
|
|
-// id: 12,
|
|
|
|
|
-// name: 'Rick Sanchez',
|
|
|
|
|
-// dimension: 'C-137',
|
|
|
|
|
-// age: 67
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `replaceById` с передачей
|
|
|
|
|
-// идентификатора и нового состава
|
|
|
|
|
-const result = await rep.replaceById(12, {
|
|
|
|
|
- name: 'Morty Smith',
|
|
|
|
|
- kind: 'a young teenage boy',
|
|
|
|
|
- age: 14,
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// вывод результата
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// id: 12,
|
|
|
|
|
-// name: 'Morty Smith', <= обновлено
|
|
|
|
|
-// kind: 'a young teenage boy', <= новое поле
|
|
|
|
|
-// age: 14 <= обновлено
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
-// поле "dimension" удалено, так как
|
|
|
|
|
-// не передавалось с новым составом
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Использование третьего параметра `filter` метода `replaceById` позволяет
|
|
|
|
|
-ограничивать количество возвращаемых данных, и разрешать связи к другим
|
|
|
|
|
-коллекциям (см. Связи).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// подготовка состава для заменяемого документа
|
|
|
|
|
-const data = {
|
|
|
|
|
- name: 'Morty Smith',
|
|
|
|
|
- kind: 'a young teenage boy',
|
|
|
|
|
- age: 14,
|
|
|
|
|
- biographyId: 61, // связь "biography" (см. belongsTo)
|
|
|
|
|
- pictureIds: [347, 348], // связь "pictures" (см. referencesMany)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// подготовка параметра "filter"
|
|
|
|
|
-const filter = {
|
|
|
|
|
- // "fields" - если определено, то результат
|
|
|
|
|
- // будут включать только указанные поля
|
|
|
|
|
- fields: [
|
|
|
|
|
- 'name',
|
|
|
|
|
- 'biographyId'
|
|
|
|
|
- 'pictureIds',
|
|
|
|
|
- ],
|
|
|
|
|
- // "include" - включение в результат
|
|
|
|
|
- // связанных документов (см. Связи)
|
|
|
|
|
- include: [
|
|
|
|
|
- 'biography',
|
|
|
|
|
- 'pictures',
|
|
|
|
|
- ],
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `replaceById` и вывод результата
|
|
|
|
|
-const result = await rep.replaceById(12, data, filter);
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// "name": "Morty Smith",
|
|
|
|
|
-// "biographyId": 61, <= новое поле
|
|
|
|
|
-// "biography": { <= разрешение связи
|
|
|
|
|
-// "id": 61,
|
|
|
|
|
-// "annotation": "This article is about Morty Smith",
|
|
|
|
|
-// "body": "Currently, Morty is 14 years old ..."
|
|
|
|
|
-// },
|
|
|
|
|
-// "pictureIds": [347, 348], <= новое поле
|
|
|
|
|
-// "picture": [ <= разрешение связи
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 347,
|
|
|
|
|
-// "mime": "image/jpeg",
|
|
|
|
|
-// "file": "/uploads/morty347.jpg"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 348,
|
|
|
|
|
-// "mime": "image/jpeg",
|
|
|
|
|
-// "file": "/uploads/morty348.jpg"
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
-// поля "kind" и "age" записаны в документ,
|
|
|
|
|
-// но исключены из результата опцией "fields"
|
|
|
|
|
-//
|
|
|
|
|
-// документы "biography" и "pictures"
|
|
|
|
|
-// встроены в результат опцией "include"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### patchById(id, data, filter = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Частично обновляет существующий документ по идентификатору и возвращает его.
|
|
|
|
|
-Если идентификатор не найден, то выбрасывает исключение.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// документ с идентификатором 24
|
|
|
|
|
-// имеет следующую структуру
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 24,
|
|
|
|
|
-// "type": "airport",
|
|
|
|
|
-// "name": "Domodedovo Airport",
|
|
|
|
|
-// "code": "DME"
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `patchById` с передачей
|
|
|
|
|
-// идентификатора и новых значений
|
|
|
|
|
-// обновляемых полей
|
|
|
|
|
-const result = await rep.patchById(24, {
|
|
|
|
|
- name: 'Sheremetyevo Airport',
|
|
|
|
|
- code: 'SVO',
|
|
|
|
|
- featured: true,
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// вывод результата
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 24,
|
|
|
|
|
-// "type": "airport",
|
|
|
|
|
-// "name": "Sheremetyevo Airport", <= обновлено
|
|
|
|
|
-// "code": "SVO", <= обновлено
|
|
|
|
|
-// "featured": true <= новое поле
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Использование третьего параметра `filter` метода `patchById` позволяет
|
|
|
|
|
-ограничивать количество возвращаемых данных, и разрешать связи к другим
|
|
|
|
|
-коллекциям (см. Связи).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// подготовка обновляемых данных
|
|
|
|
|
-const data = {
|
|
|
|
|
- name: 'Sheremetyevo Airport',
|
|
|
|
|
- code: 'SVO',
|
|
|
|
|
- featured: true,
|
|
|
|
|
- cityId: 231, // связь "city" (см. belongsTo)
|
|
|
|
|
- companyIds: [513, 514], // связь "companies" (см. referencesMany)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// подготовка параметра `filter`
|
|
|
|
|
-const filter = {
|
|
|
|
|
- // "fields" - если определено, то результат
|
|
|
|
|
- // будут включать только указанные поля
|
|
|
|
|
- fields: [
|
|
|
|
|
- 'name',
|
|
|
|
|
- 'cityId',
|
|
|
|
|
- 'companyIds',
|
|
|
|
|
- ],
|
|
|
|
|
- // "include" - включение в результат
|
|
|
|
|
- // связанных документов (см. Связи)
|
|
|
|
|
- include: [
|
|
|
|
|
- 'city',
|
|
|
|
|
- 'companies',
|
|
|
|
|
- ],
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `patchById` и вывод результата
|
|
|
|
|
-const result = await rep.patchById(24, data, filter);
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 24,
|
|
|
|
|
-// "name": "Sheremetyevo Airport", <= обновлено
|
|
|
|
|
-// "cityId": 231, <= новое поле
|
|
|
|
|
-// "city": { <= разрешение связи
|
|
|
|
|
-// "id": 231,
|
|
|
|
|
-// "name": "Moscow"
|
|
|
|
|
-// },
|
|
|
|
|
-// "companyIds": [513, 514], <= новое поле
|
|
|
|
|
-// "companies": [ <= разрешение связи
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 513,
|
|
|
|
|
-// "name": "S7 Airlines"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 514,
|
|
|
|
|
-// "name": "Aeroflot Airlines"
|
|
|
|
|
-// },
|
|
|
|
|
-// ]
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
-// поля "code" и "featured" обновлены,
|
|
|
|
|
-// но исключены из ответа опцией "fields"
|
|
|
|
|
-//
|
|
|
|
|
-// документы "city" и "companies"
|
|
|
|
|
-// встроены в результат опцией "include"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### patch(data, where = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Обновляет документы и возвращает их число. Используется для
|
|
|
|
|
-обновления нескольких документов согласно условиям выборки,
|
|
|
|
|
-или всей коллекции сразу.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция имеет три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "type": "city",
|
|
|
|
|
-// "name": "Bangkok",
|
|
|
|
|
-// "hidden": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "type": "country",
|
|
|
|
|
-// "name": "Thailand",
|
|
|
|
|
-// "hidden": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "type": "city",
|
|
|
|
|
-// "name": "Moscow",
|
|
|
|
|
-// "hidden": true
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `patch` с передачей
|
|
|
|
|
-// значений для обновляемых полей
|
|
|
|
|
-const result = await rep.patch({
|
|
|
|
|
- hidden: false,
|
|
|
|
|
- updatedAt: new Date().toISOString(),
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// вывод количество затронутых документов
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// 3
|
|
|
|
|
-
|
|
|
|
|
-// просмотр коллекции методом `find`
|
|
|
|
|
-// для проверки изменений
|
|
|
|
|
-const docs = await rep.find();
|
|
|
|
|
-console.log(docs);
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "type": "city",
|
|
|
|
|
-// "name": "Bangkok",
|
|
|
|
|
-// "hidden": false, <= обновлено
|
|
|
|
|
-// "updatedAt": "2023-12-02T14:00:00.000Z" <= новое поле
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "type": "country",
|
|
|
|
|
-// "name": "Thailand",
|
|
|
|
|
-// "hidden": false, <= обновлено
|
|
|
|
|
-// "updatedAt": "2023-12-02T14:00:00.000Z" <= новое поле
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "type": "city",
|
|
|
|
|
-// "name": "Moscow",
|
|
|
|
|
-// "hidden": false, <= обновлено
|
|
|
|
|
-// "updatedAt": "2023-12-02T14:00:00.000Z" <= новое поле
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Использование второго параметра `where` метода `patch` позволяет
|
|
|
|
|
-определить условия выборки обновляемых документов.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// вызов метода `patch` с передачей
|
|
|
|
|
-// условий выборки вторым параметром
|
|
|
|
|
-const result = await rep.patch(
|
|
|
|
|
- // новые значения полей
|
|
|
|
|
- {
|
|
|
|
|
- hidden: false,
|
|
|
|
|
- updatedAt: new Date().toISOString(),
|
|
|
|
|
- },
|
|
|
|
|
- // условия выборки
|
|
|
|
|
- {
|
|
|
|
|
- type: 'city', // только если "type" равен "city"
|
|
|
|
|
- hidden: true, // и "hidden" имеет значение true
|
|
|
|
|
- }
|
|
|
|
|
-);
|
|
|
|
|
-
|
|
|
|
|
-// вывод количества затронутых документов
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// 2
|
|
|
|
|
-
|
|
|
|
|
-// по условиям выборки обновлено
|
|
|
|
|
-// только 2 документа из 3-х
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### find(filter = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Возвращает все документы коллекции или согласно условию.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// вызов метода `find` без аргументов
|
|
|
|
|
-// возвращает все документы коллекции
|
|
|
|
|
-const result = await rep.find();
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "type": "article",
|
|
|
|
|
-// "title": "The Forgotten Ship",
|
|
|
|
|
-// "publishedAt": "2023-12-02T08:00:00.000Z",
|
|
|
|
|
-// "featured": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "type": "article",
|
|
|
|
|
-// "title": "A Giant Bellows",
|
|
|
|
|
-// "publishedAt": "2023-12-02T12:00:00.000Z",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "type": "letter",
|
|
|
|
|
-// "title": "Hundreds of bottles",
|
|
|
|
|
-// "publishedAt": null,
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Фильтрация результата (опционально).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// первый параметр может принимать
|
|
|
|
|
-// объект cо следующими настройками
|
|
|
|
|
-const result = await rep.find({
|
|
|
|
|
- // "where" - фильтрация выборки по условию, где
|
|
|
|
|
- // указанные поля должны содержать определенные
|
|
|
|
|
- // значения (см. Фильтрация)
|
|
|
|
|
- where: {type: 'article', featured: true},
|
|
|
|
|
- where: {title: {like: 'the forgotten'}},
|
|
|
|
|
- where: {publishedAt: {lte: '2023-12-02T21:00:00.000Z'}},
|
|
|
|
|
-
|
|
|
|
|
- // "order" - сортировка по указанному полю может
|
|
|
|
|
- // принимать постфикс DESC для обратного порядка
|
|
|
|
|
- order: 'featured',
|
|
|
|
|
- order: 'publishedAt DESC',
|
|
|
|
|
- order: ['publishedAt DESC', 'featured ASC', 'id'],
|
|
|
|
|
-
|
|
|
|
|
- // "limit" - ограничение результата
|
|
|
|
|
- // "skip" - пропуск документов
|
|
|
|
|
- limit: 10,
|
|
|
|
|
- skip: 10,
|
|
|
|
|
-
|
|
|
|
|
- // "fields" - если определено, то результат
|
|
|
|
|
- // будет включать только указанные поля
|
|
|
|
|
- fields: 'title',
|
|
|
|
|
- fields: ['title', 'featured'],
|
|
|
|
|
-
|
|
|
|
|
- // "include" - включить в результат связанные
|
|
|
|
|
- // документы (см. Связи)
|
|
|
|
|
- include: 'author',
|
|
|
|
|
- include: {author: 'role'},
|
|
|
|
|
- include: ['author', 'categories'],
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### findOne(filter = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Возвращает первый найденный документ или `undefined`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция имеет три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "type": "article",
|
|
|
|
|
-// "title": "The Forgotten Ship",
|
|
|
|
|
-// "publishedAt": "2023-12-02T08:00:00.000Z",
|
|
|
|
|
-// "featured": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "type": "article",
|
|
|
|
|
-// "title": "A Giant Bellows",
|
|
|
|
|
-// "publishedAt": "2023-12-02T12:00:00.000Z",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "type": "letter",
|
|
|
|
|
-// "title": "Hundreds of bottles",
|
|
|
|
|
-// "publishedAt": null,
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `findOne` без аргументов
|
|
|
|
|
-// возвращает первый документ коллекции
|
|
|
|
|
-const result = await rep.findOne();
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "type": "article",
|
|
|
|
|
-// "title": "The Forgotten Ship",
|
|
|
|
|
-// "publishedAt": "2023-12-02T08:00:00.000Z",
|
|
|
|
|
-// "featured": true
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Фильтрация результата (опционально).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// первый параметр метода `find` принимает
|
|
|
|
|
-// объект настроек возвращаемого результата
|
|
|
|
|
-const result = await rep.findOne({
|
|
|
|
|
- // "where" - фильтрация выборки по условию, где
|
|
|
|
|
- // указанные поля должны содержать определенные
|
|
|
|
|
- // значения (см. Фильтрация)
|
|
|
|
|
- where: {type: 'article', featured: true},
|
|
|
|
|
- where: {title: {like: 'the forgotten'}},
|
|
|
|
|
- where: {publishedAt: {lte: '2023-12-02T21:00:00.000Z'}},
|
|
|
|
|
-
|
|
|
|
|
- // "order" - сортировка по указанному полю может
|
|
|
|
|
- // принимать постфикс DESC для обратного порядка
|
|
|
|
|
- order: 'featured',
|
|
|
|
|
- order: 'publishedAt DESC',
|
|
|
|
|
- order: ['publishedAt DESC', 'featured ASC', 'id'],
|
|
|
|
|
-
|
|
|
|
|
- // "skip" - пропуск документов
|
|
|
|
|
- skip: 10,
|
|
|
|
|
-
|
|
|
|
|
- // "fields" - если определено, то результат
|
|
|
|
|
- // будет включать только указанные поля
|
|
|
|
|
- fields: 'title',
|
|
|
|
|
- fields: ['title', 'featured'],
|
|
|
|
|
-
|
|
|
|
|
- // "include" - включить в результат связанные
|
|
|
|
|
- // документы (см. Связи)
|
|
|
|
|
- include: 'author',
|
|
|
|
|
- include: {author: 'role'},
|
|
|
|
|
- include: ['author', 'categories'],
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### findById(id, filter = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Поиск документа по идентификатору. Возвращает найденный документ
|
|
|
|
|
-или выбрасывает исключение.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция содержит три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "title": "The Forgotten Ship",
|
|
|
|
|
-// "featured": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "title": "A Giant Bellows",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "title": "Hundreds of bottles",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `findById` с передачей
|
|
|
|
|
-// идентификатора искомого документа
|
|
|
|
|
-const result = await rep.findById(2);
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "title": "A Giant Bellows",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// }
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Использование параметра `filter` (опционально).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// второй параметр метода `findById` принимает
|
|
|
|
|
-// объект настроек возвращаемого результата
|
|
|
|
|
-const result = await rep.findById(2, {
|
|
|
|
|
- // "fields" - если определено, то результат
|
|
|
|
|
- // будут включать только указанные поля
|
|
|
|
|
- fields: 'title',
|
|
|
|
|
- fields: ['title', 'featured'],
|
|
|
|
|
-
|
|
|
|
|
- // "include" - включить в результат связанные
|
|
|
|
|
- // документы (см. Связи)
|
|
|
|
|
- include: 'author',
|
|
|
|
|
- include: {author: 'role'},
|
|
|
|
|
- include: ['author', 'categories'],
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### delete(where = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Удаляет все документы коллекции или согласно условию. Возвращает количество
|
|
|
|
|
-удаленных документов.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция имеет три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "title": "The Forgotten Ship",
|
|
|
|
|
-// "featured": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "title": "A Giant Bellows",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "title": "Hundreds of bottles",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `delete` без аргументов
|
|
|
|
|
-// удалит все содержимое коллекции
|
|
|
|
|
-const result = await rep.delete();
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// 3
|
|
|
|
|
-
|
|
|
|
|
-// просмотр коллекции методом `find`
|
|
|
|
|
-// для проверки изменений
|
|
|
|
|
-const docs = await rep.find();
|
|
|
|
|
-console.log(docs);
|
|
|
|
|
-// []
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Условия выборки (опционально).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// первый параметр метода `delete`
|
|
|
|
|
-// принимает условия выборки
|
|
|
|
|
-const result = await rep.delete({
|
|
|
|
|
- title: {
|
|
|
|
|
- like: 'bellows', // оператор "like" проверяет поле "title"
|
|
|
|
|
- }, // на содержание подстроки "bellows"
|
|
|
|
|
- featured: false, // значение поля "featured" должно быть false
|
|
|
|
|
- // см. Фильтрация
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// вывод результата
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// 1
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### deleteById(id)
|
|
|
|
|
-
|
|
|
|
|
-Удаляет документ по идентификатору. Возвращает `true` в случае успеха
|
|
|
|
|
-или `false` если не найден.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция имеет три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "title": "The Forgotten Ship"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "title": "A Giant Bellows"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "title": "Hundreds of bottles"
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `deleteById` с передачей
|
|
|
|
|
-// идентификатора удаляемого документа
|
|
|
|
|
-const result = await rep.deleteById(2);
|
|
|
|
|
-
|
|
|
|
|
-// вывод результата
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// true
|
|
|
|
|
-
|
|
|
|
|
-// просмотр коллекции методом `find`
|
|
|
|
|
-// для проверки изменений
|
|
|
|
|
-const docs = await rep.find();
|
|
|
|
|
-console.log(docs);
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "title": "The Forgotten Ship"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "title": "Hundreds of bottles"
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### exists(id)
|
|
|
|
|
-
|
|
|
|
|
-Проверка существования документа по идентификатору. Возвращает `true`
|
|
|
|
|
-если найден, в противном случае `false`.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция имеет три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "title": "The Forgotten Ship"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "title": "A Giant Bellows"
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "title": "Hundreds of bottles"
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `exists` с передачей
|
|
|
|
|
-// существующего идентификатора
|
|
|
|
|
-const result1 = await rep.exists(2);
|
|
|
|
|
-console.log(result1);
|
|
|
|
|
-// true
|
|
|
|
|
-
|
|
|
|
|
-// результат проверки несуществующего
|
|
|
|
|
-// идентификатора
|
|
|
|
|
-const result2 = await rep.exists(10);
|
|
|
|
|
-console.log(result2);
|
|
|
|
|
-// false
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### count(where = undefined)
|
|
|
|
|
-
|
|
|
|
|
-Подсчет количества документов и возврат их числа.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// коллекция имеет три документа
|
|
|
|
|
-// [
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 1,
|
|
|
|
|
-// "title": "The Forgotten Ship",
|
|
|
|
|
-// "featured": true
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 2,
|
|
|
|
|
-// "title": "A Giant Bellows",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// },
|
|
|
|
|
-// {
|
|
|
|
|
-// "id": 3,
|
|
|
|
|
-// "title": "Hundreds of bottles",
|
|
|
|
|
-// "featured": false
|
|
|
|
|
-// }
|
|
|
|
|
-// ]
|
|
|
|
|
-
|
|
|
|
|
-// вызов метода `count` без аргументов
|
|
|
|
|
-// возвращает общее число документов
|
|
|
|
|
-const result = await rep.count();
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// 3
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Условия выборки (опционально).
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-// первый параметр метода `count`
|
|
|
|
|
-// принимает условия выборки
|
|
|
|
|
-const result = await rep.count({
|
|
|
|
|
- featured: { // оператор "neq" проверяет поле "featured"
|
|
|
|
|
- neq: true, // на неравенство значению true
|
|
|
|
|
- }
|
|
|
|
|
- // см. Фильтрация
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// вывод результата
|
|
|
|
|
-console.log(result);
|
|
|
|
|
-// 2
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Расширение
|
|
|
|
|
-
|
|
|
|
|
-При использовании метода `getRepository` выполняется проверка на
|
|
|
|
|
-наличие существующего экземпляра репозитория для нужной модели.
|
|
|
|
|
-При первичном запросе создается новый экземпляр, который будет
|
|
|
|
|
-сохранен для повторных обращений к методу.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-import {Schema} from '@e22m4u/js-repository';
|
|
|
|
|
-import {Repository} from '@e22m4u/js-repository';
|
|
|
|
|
-
|
|
|
|
|
-// const schema = new Schema();
|
|
|
|
|
-// schema.defineDatasource ...
|
|
|
|
|
-// schema.defineModel ...
|
|
|
|
|
-
|
|
|
|
|
-const rep1 = schema.getRepository('myModel');
|
|
|
|
|
-const rep2 = schema.getRepository('myModel');
|
|
|
|
|
-console.log(rep1 === rep2); // true
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Если требуется изменить или расширить поведение репозитория, то
|
|
|
|
|
-перед его созданием можно переопределить его конструктор методом
|
|
|
|
|
-`setRepositoryCtor`. После чего, все создаваемые репозитории будут
|
|
|
|
|
-использовать уже пользовательский конструктор вместо стандартного.
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-import {Schema} from '@e22m4u/js-repository';
|
|
|
|
|
-import {Repository} from '@e22m4u/js-repository';
|
|
|
|
|
-import {RepositoryRegistry} from '@e22m4u/js-repository';
|
|
|
|
|
-
|
|
|
|
|
-class MyRepository extends Repository {
|
|
|
|
|
- /*...*/
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const schema = new Schema();
|
|
|
|
|
-schema.get(RepositoryRegistry).setRepositoryCtor(MyRepository);
|
|
|
|
|
-// теперь schema.getRepository(modelName) будет возвращать
|
|
|
|
|
-// экземпляр класса MyRepository
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-### Filter
|
|
|
|
|
-
|
|
|
|
|
-Большинство методов репозитория принимают объект `filter` для
|
|
|
|
|
-фильтрации возвращаемого ответа. Описание параметров объекта:
|
|
|
|
|
-
|
|
|
|
|
-- **where** *(условия выборки данных из базы)*
|
|
|
|
|
- примеры:
|
|
|
|
|
- `where: {foo: 'bar'}` поиск по значению поля `foo`
|
|
|
|
|
- `where: {foo: {eq: 'bar'}}` оператор равенства `eq`
|
|
|
|
|
- `where: {foo: {neq: 'bar'}}` оператор неравенства `neq`
|
|
|
|
|
- `where: {foo: {gt: 5}}` оператор "больше" `gt`
|
|
|
|
|
- `where: {foo: {lt: 10}}` оператор "меньше" `lt`
|
|
|
|
|
- `where: {foo: {gte: 5}}` оператор "больше или равно" `gte`
|
|
|
|
|
- `where: {foo: {lte: 10}}` оператор "меньше или равно" `lte`
|
|
|
|
|
- `where: {foo: {inq: ['bar', 'baz']}}` равенство одного из значений `inq`
|
|
|
|
|
- `where: {foo: {nin: ['bar', 'baz']}}` исключение значений массива `nin`
|
|
|
|
|
- `where: {foo: {between: [5, 10]}}` оператор диапазона `between`
|
|
|
|
|
- `where: {foo: {exists: true}}` оператор наличия значения `exists`
|
|
|
|
|
- `where: {foo: {like: 'bar'}}` оператор поиска подстроки `like`
|
|
|
|
|
- `where: {foo: {ilike: 'BaR'}}` регистронезависимая версия `ilike`
|
|
|
|
|
- `where: {foo: {nlike: 'bar'}}` оператор исключения подстроки `nlike`
|
|
|
|
|
- `where: {foo: {nilike: 'BaR'}}` регистронезависимая версия `nilike`
|
|
|
|
|
- `where: {foo: {regexp: 'ba.+'}}` оператор регулярного выражения `regexp`
|
|
|
|
|
- `where: {foo: {regexp: 'ba.+', flags: 'i'}}` флаги регулярного выражения
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-- **order** *(упорядочить записи по полю)*
|
|
|
|
|
- примеры:
|
|
|
|
|
- `order: 'foo'` порядок по полю `foo`
|
|
|
|
|
- `order: 'foo ASC'` явное указание порядка
|
|
|
|
|
- `order: 'foo DESC'` инвертировать порядок
|
|
|
|
|
- `order: ['foo', 'bar ASC', 'baz DESC']` по нескольким полям
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-- **limit** *(не более N записей)*
|
|
|
|
|
- примеры:
|
|
|
|
|
- `limit: 0` не ограничивать
|
|
|
|
|
- `limit: 14` не более 14-и
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-- **skip** *(пропуск первых N записей)*
|
|
|
|
|
- примеры:
|
|
|
|
|
- `skip: 0` выборка без пропуска
|
|
|
|
|
- `skip: 10` пропустить 10 объектов выборки
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-- **include** *(включение связанных данных в результат)*
|
|
|
|
|
- примеры:
|
|
|
|
|
- `include: 'foo'` включение связи `foo`
|
|
|
|
|
- `include: ['foo', 'bar']` включение `foo` и `bar`
|
|
|
|
|
- `include: {foo: 'bar'}` включение вложенной связи `foo`
|
|
|
|
|
-
|
|
|
|
|
-## Связи
|
|
|
|
|
-
|
|
|
|
|
-Параметр `relations` описывает набор связей к другим моделям.
|
|
|
|
|
-
|
|
|
|
|
-Понятия:
|
|
|
|
|
-
|
|
|
|
|
-- источник связи
|
|
|
|
|
-*- модель в которой определена данная связь*
|
|
|
|
|
-- целевая модель
|
|
|
|
|
-*- модель на которую ссылается источник связи*
|
|
|
|
|
-
|
|
|
|
|
-Типы:
|
|
|
|
|
-
|
|
|
|
|
-- `belongsTo` - ссылка на целевую модель находится в источнике
|
|
|
|
|
-- `hasOne` - ссылка на источник находится в целевой модели (one-to-one)
|
|
|
|
|
-- `hasMany` - ссылка на источник находится в целевой модели (one-to-many)
|
|
|
|
|
-- `referencesMany` - массив ссылок на целевую модель находится в источнике
|
|
|
|
|
-
|
|
|
|
|
-Параметры:
|
|
|
|
|
-
|
|
|
|
|
-- `type: string` тип связи
|
|
|
|
|
-- `model: string` целевая модель
|
|
|
|
|
-- `foreignKey: string` поле для идентификатора цели
|
|
|
|
|
-- `polymorphic: boolean|string` объявить связь полиморфной*
|
|
|
|
|
-- `discriminator: string` поле для названия целевой модели (для `polymorphic: true`)
|
|
|
|
|
-
|
|
|
|
|
-*i. Полиморфный режим `belongsTo` позволяет динамически определять цель связи,
|
|
|
|
|
-где имя целевой модели хранится в отдельном поле, рядом с `foreignKey`*
|
|
|
|
|
-
|
|
|
|
|
-#### BelongsTo
|
|
|
|
|
-
|
|
|
|
|
-Связь заказа к покупателю через поле `customerId`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- customer: {
|
|
|
|
|
- type: 'belongsTo',
|
|
|
|
|
- model: 'customer',
|
|
|
|
|
- foreignKey: 'customerId', // опционально
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Полиморфная версия
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- customer: {
|
|
|
|
|
- type: 'belongsTo',
|
|
|
|
|
- polymorphic: true,
|
|
|
|
|
- foreignKey: 'customerId', // опционально
|
|
|
|
|
- discriminator: 'customerType', // опционально
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### HasOne (one-to-one)
|
|
|
|
|
-
|
|
|
|
|
-Связь покупателя к заказу, как обратная сторона `belongsTo`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- order: {
|
|
|
|
|
- type: 'hasOne',
|
|
|
|
|
- model: 'order',
|
|
|
|
|
- foreignKey: 'customerId', // поле целевой модели
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Обратная сторона полиморфной версии `belongsTo`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- order: {
|
|
|
|
|
- type: 'hasOne',
|
|
|
|
|
- model: 'order',
|
|
|
|
|
- polymorphic: 'customer', // имя связи целевой модели
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Явное указание `foreignKey` и `discriminator`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- order: {
|
|
|
|
|
- type: 'hasOne',
|
|
|
|
|
- model: 'order',
|
|
|
|
|
- polymorphic: true, // true вместо имени модели
|
|
|
|
|
- foreignKey: 'customerId', // поле целевой модели
|
|
|
|
|
- discriminator: 'customerType', // поле целевой модели
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### HasMany (one-to-many)
|
|
|
|
|
-
|
|
|
|
|
-Связь покупателя к заказам, как обратная сторона `belongsTo`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- orders: {
|
|
|
|
|
- type: 'hasMany',
|
|
|
|
|
- model: 'order',
|
|
|
|
|
- foreignKey: 'customerId', // поле целевой модели
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Обратная сторона полиморфной версии `belongsTo`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- orders: {
|
|
|
|
|
- type: 'hasMany',
|
|
|
|
|
- model: 'order',
|
|
|
|
|
- polymorphic: 'customer', // имя связи целевой модели
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Явное указание `foreignKey` и `discriminator`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- orders: {
|
|
|
|
|
- type: 'hasMany',
|
|
|
|
|
- model: 'order',
|
|
|
|
|
- polymorphic: true, // true вместо имени модели
|
|
|
|
|
- foreignKey: 'customerId', // поле целевой модели
|
|
|
|
|
- discriminator: 'customerType', // поле целевой модели
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-#### ReferencesMany
|
|
|
|
|
-
|
|
|
|
|
-Связь покупателя к заказам через поле `orderIds`
|
|
|
|
|
-
|
|
|
|
|
-```js
|
|
|
|
|
-schema.defineModel({
|
|
|
|
|
- // ...
|
|
|
|
|
- relations: {
|
|
|
|
|
- // ...
|
|
|
|
|
- orders: {
|
|
|
|
|
- type: 'referencesMany',
|
|
|
|
|
- model: 'order',
|
|
|
|
|
- foreignKey: 'orderIds', // опционально
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
-});
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
--->
|
|
|
|
|
-
|
|
|
|
|
-## Тесты
|
|
|
|
|
-
|
|
|
|
|
-```bash
|
|
|
|
|
-npm run test
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Лицензия
|
|
|
|
|
-
|
|
|
|
|
-MIT
|
|
|