@e22m4u/js-repository 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  ## @e22m4u/js-repository
2
2
 
3
- Реализация паттерна «Репозиторий» для работы с базами данных в Node.js
3
+ ![npm version](https://badge.fury.io/js/@e22m4u%2Fjs-repository.svg)
4
+ ![license](https://img.shields.io/badge/license-mit-blue.svg)
5
+
6
+ Реализация паттерна «Репозиторий» для работы с базами данных.
7
+
8
+ ## Содержание
4
9
 
5
10
  - [Установка](#установка)
6
11
  - [Импорт](#импорт)
@@ -21,8 +26,27 @@
21
26
  - [Пустые значения](#пустые-значения)
22
27
  - [Переопределение пустых значений](#переопределение-пустых-значений)
23
28
  - [Репозиторий](#репозиторий)
29
+ - [create](#repositorycreate)
30
+ - [replaceById](#repositoryreplacebyid)
31
+ - [replaceOrCreate](#repositoryreplaceorcreate)
32
+ - [patchById](#repositorypatchbyid)
33
+ - [patch](#repositorypatch)
34
+ - [find](#repositoryfind)
35
+ - [findOne](#repositoryfindone)
36
+ - [findById](#repositoryfindbyid)
37
+ - [delete](#repositorydelete)
38
+ - [deleteById](#repositorydeletebyid)
39
+ - [exists](#repositoryexists)
40
+ - [count](#repositorycount)
24
41
  - [Фильтрация](#фильтрация)
25
42
  - [Связи](#связи)
43
+ - [Belongs To](#belongs-to)
44
+ - [Has One](#has-one)
45
+ - [Has Many](#has-many)
46
+ - [References Many](#references-many)
47
+ - [Belongs To (полиморфная)](#belongs-to-полиморфная-версия)
48
+ - [Has One (полиморфная)](#has-one-полиморфная-версия)
49
+ - [Has Many (полиморфная)](#has-many-полиморфная-версия)
26
50
  - [Расширение](#расширение)
27
51
  - [TypeScript](#typescript)
28
52
  - [Тесты](#тесты)
@@ -36,10 +60,10 @@ npm install @e22m4u/js-repository
36
60
 
37
61
  Опционально устанавливается нужный адаптер.
38
62
 
39
- | адаптер | описание |
40
- |-----------|--------------------------------------------------------------------------------------------------------------------------------|
41
- | `memory` | Виртуальная база в памяти процесса (не требует установки) |
42
- | `mongodb` | MongoDB - система управления NoSQL базами (*[установка](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
63
+ | адаптер | описание | установка |
64
+ |-----------|-------------------------------------------------|----------------------------------------------------------------------------|
65
+ | `memory` | Виртуальная база в памяти процесса | *встроенный* |
66
+ | `mongodb` | MongoDB - документо-ориентированная база данных | [npm](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter) |
43
67
 
44
68
  ## Импорт
45
69
 
@@ -68,7 +92,7 @@ const {DatabaseSchema} = require('@e22m4u/js-repository');
68
92
  «один к одному», «один ко многим» и другие типы отношений между моделями.
69
93
 
70
94
  Непосредственно чтение и запись данных производится с помощью *репозитория*,
71
- который имеет каждая модель с объявленным *источником данных*. Репозиторий
95
+ который есть у каждой модели с объявленным *источником данных*. Репозиторий
72
96
  может фильтровать запрашиваемые документы, выполнять валидацию свойств
73
97
  согласно определению модели, и встраивать связанные данные в результат
74
98
  выборки.
@@ -111,7 +135,18 @@ flowchart TD
111
135
 
112
136
  ## Пример
113
137
 
114
- Объявление источника данных, модели и добавление нового документа в коллекцию.
138
+ Пример демонстрирует создание экземпляра схемы, объявление источника данных
139
+ и модели `country`. После чего, с помощью репозитория данной модели, в коллекцию
140
+ добавляется новый документ (страна), который выводится в консоль.
141
+
142
+ ```
143
+ Страна (country)
144
+ ┌─────────────────────────┐
145
+ │ id: 1 │
146
+ │ name: "Russia" │
147
+ │ population: 143400000 │
148
+ └─────────────────────────┘
149
+ ```
115
150
 
116
151
  ```js
117
152
  import {DataType} from '@e22m4u/js-repository';
@@ -120,18 +155,18 @@ import {DatabaseSchema} from '@e22m4u/js-repository';
120
155
  // создание экземпляра DatabaseSchema
121
156
  const dbs = new DatabaseSchema();
122
157
 
123
- // объявление источника "myMemory"
158
+ // объявление источника "myDb"
124
159
  dbs.defineDatasource({
125
- name: 'myMemory', // название нового источника
160
+ name: 'myDb', // название нового источника
126
161
  adapter: 'memory', // выбранный адаптер
127
162
  });
128
163
 
129
164
  // объявление модели "country"
130
165
  dbs.defineModel({
131
- name: 'country', // название новой модели
132
- datasource: 'myMemory', // выбранный источник
133
- properties: { // свойства модели
134
- name: DataType.STRING, // тип "string"
166
+ name: 'country', // название новой модели
167
+ datasource: 'myDb', // выбранный источник
168
+ properties: { // свойства модели
169
+ name: DataType.STRING, // тип "string"
135
170
  population: DataType.NUMBER, // тип "number"
136
171
  },
137
172
  })
@@ -154,6 +189,81 @@ console.log(country);
154
189
  // }
155
190
  ```
156
191
 
192
+ В следующем блоке определяется модель `city` со связью `belongsTo` к модели
193
+ `country` из примера выше. Затем создается новый документ города, связанный
194
+ с ранее созданной страной. После создания нового документа, выполняется запрос
195
+ на извлечение данного города с включением связанной страны.
196
+
197
+ ```
198
+ Страна (country) Город (city)
199
+ ┌─────────────────────────┐ ┌─────────────────────────┐
200
+ │ id: 1 <───────────────│───┐ │ id: 1 │
201
+ │ name: "Russia" │ │ │ name: "Moscow" │
202
+ │ population: 143400000 │ └───│─ countryId: 1 │
203
+ └─────────────────────────┘ └─────────────────────────┘
204
+ ```
205
+
206
+ ```js
207
+ // объявление модели "city" со связью к "country"
208
+ dbs.defineModel({
209
+ name: 'city',
210
+ datasource: 'myDb',
211
+ properties: {
212
+ name: DataType.STRING,
213
+ countryId: DataType.NUMBER,
214
+ // внешний ключ "countryId" указывать не обязательно,
215
+ // но для проверки типа значения перед записью в базу
216
+ // рекомендуется, так как адаптер "memory" по умолчанию
217
+ // создает числовые идентификаторы
218
+ },
219
+ relations: {
220
+ // определение связи "country" позволит автоматически включать
221
+ // связанные документы с помощью опции "include" при запросах
222
+ // из данной коллекции через методы репозитория
223
+ country: {
224
+ type: RelationType.BELONGS_TO, // тип связи: принадлежит к...
225
+ model: 'country', // название целевой модели
226
+ foreignKey: 'countryId', // поле с внешним ключом (не обязательно)
227
+ // если внешний ключ соответствует `relationName` + `Id`,
228
+ // то указывать опцию `foreignKey` не обязательно
229
+ },
230
+ },
231
+ });
232
+
233
+ // получение репозитория для модели "city"
234
+ const cityRep = dbs.getRepository('city');
235
+
236
+ // создание нового города и его привязка к стране через country.id
237
+ const city = await cityRep.create({
238
+ name: 'Moscow',
239
+ countryId: country.id, // использование id созданной ранее страны
240
+ });
241
+
242
+ console.log(city);
243
+ // {
244
+ // id: 1,
245
+ // name: 'Moscow',
246
+ // countryId: 1,
247
+ // }
248
+
249
+ // извлечение города по идентификатору с включением связанной страны
250
+ const cityWithCountry = await cityRep.findById(city.id, {
251
+ include: 'country',
252
+ });
253
+
254
+ console.log(cityWithCountry);
255
+ // {
256
+ // id: 1,
257
+ // name: 'Moscow',
258
+ // countryId: 1,
259
+ // country: {
260
+ // id: 1,
261
+ // name: 'Russia',
262
+ // population: 143400000
263
+ // }
264
+ // }
265
+ ```
266
+
157
267
  ## Схема
158
268
 
159
269
  Экземпляр класса `DatabaseSchema` хранит определения источников данных и моделей.
@@ -178,7 +288,7 @@ const dbs = new DatabaseSchema();
178
288
 
179
289
  ```js
180
290
  dbs.defineDatasource({
181
- name: 'myMemory', // название нового источника
291
+ name: 'myDb', // название нового источника
182
292
  adapter: 'memory', // выбранный адаптер
183
293
  });
184
294
  ```
@@ -188,7 +298,7 @@ dbs.defineDatasource({
188
298
  ```js
189
299
  dbs.defineModel({
190
300
  name: 'product', // название новой модели
191
- datasource: 'myMemory', // выбранный источник
301
+ datasource: 'myDb', // выбранный источник
192
302
  properties: { // свойства модели
193
303
  name: DataType.STRING,
194
304
  weight: DataType.NUMBER,
@@ -220,16 +330,16 @@ const productRep = dbs.getRepository('product');
220
330
 
221
331
  ```js
222
332
  dbs.defineDatasource({
223
- name: 'myMemory', // название нового источника
333
+ name: 'myDb', // название нового источника
224
334
  adapter: 'memory', // выбранный адаптер
225
335
  });
226
336
  ```
227
337
 
228
- Передача дополнительных параметров адаптера.
338
+ Передача дополнительных параметров на примере MongoDB адаптера *([установка](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))*.
229
339
 
230
340
  ```js
231
341
  dbs.defineDatasource({
232
- name: 'myMongodb',
342
+ name: 'myDb',
233
343
  adapter: 'mongodb',
234
344
  // параметры адаптера "mongodb"
235
345
  host: '127.0.0.1',
@@ -298,8 +408,8 @@ dbs.defineModel({
298
408
 
299
409
  Если значением параметра `unique` является `true` или `'strict'`, то выполняется
300
410
  строгая проверка на уникальность. В этом режиме [пустые значения](#Пустые-значения)
301
- так же подлежат проверке, где `null` и `undefined` не могут повторяться более одного
302
- раза.
411
+ так же подлежат проверке, где `null` и `undefined` также считаются значениями,
412
+ которые должны быть уникальными.
303
413
 
304
414
  Режим `'sparse'` проверяет только значения с полезной нагрузкой, исключая
305
415
  [пустые значения](#Пустые-значения), список которых отличается в зависимости
@@ -395,8 +505,8 @@ dbs.defineModel({
395
505
 
396
506
  Валидаторы указанные ниже находятся в разработке:
397
507
 
398
- - `isLowerCase` проверка регистра (только прописные буквы);
399
- - `isUpperCase` проверка регистра (только строчные буквы);
508
+ - `isLowerCase` проверка регистра (только строчные буквы);
509
+ - `isUpperCase` проверка регистра (только прописные буквы);
400
510
  - `isEmail` проверка формата электронного адреса;
401
511
 
402
512
  **Примеры**
@@ -493,7 +603,7 @@ import {Errorf} from '@e22m4u/js-format';
493
603
  import {PropertyValidatorRegistry} from '@e22m4u/js-repository';
494
604
 
495
605
  // получение экземпляра сервиса
496
- const pvr = dbs.get(PropertyValidatorRegistry);
606
+ const pvr = dbs.getService(PropertyValidatorRegistry);
497
607
 
498
608
  // регулярные выражения для разных версий UUID
499
609
  const uuidRegex = {
@@ -762,7 +872,7 @@ dbs.defineModel({
762
872
  import {PropertyTransformerRegistry} from '@e22m4u/js-repository';
763
873
 
764
874
  // получение экземпляра сервиса
765
- const ptr = dbs.get(PropertyTransformerRegistry);
875
+ const ptr = dbs.getService(PropertyTransformerRegistry);
766
876
 
767
877
  // регистрация глобального трансформера "stripTags"
768
878
  ptr.addTransformer('stripTags', (value, options, context) => {
@@ -891,13 +1001,13 @@ dbs.defineModel({
891
1001
 
892
1002
  ## Пустые значения
893
1003
 
894
- Разные типы свойств имеют свои наборы пустых значений. Эти наборы
895
- используются для определения наличия полезной нагрузки в значении
896
- свойства. Например, параметр `default` в определении свойства
897
- устанавливает значение по умолчанию, только если входящее значение
898
- является пустым. Параметр `required` исключает пустые значения
899
- выбрасывая ошибку. А параметр `unique` в режиме `sparse` наоборот
900
- допускает дублирование пустых значений уникального свойства.
1004
+ Разные типы свойств имеют свои наборы пустых значений. Эти наборы используются
1005
+ для определения наличия полезной нагрузки в значении свойства. Например,
1006
+ параметр `default` в определении свойства устанавливает значение по умолчанию,
1007
+ только если входящее значение является пустым. Параметр `required` исключает
1008
+ пустые значения выбрасывая ошибку. А параметр `unique` в режиме `sparse`
1009
+ наоборот допускает дублирование пустых значений уникального свойства,
1010
+ поскольку они не участвуют в проверке.
901
1011
 
902
1012
  | тип | пустые значения |
903
1013
  |-------------|---------------------------|
@@ -966,23 +1076,23 @@ emptyValuesService.setEmptyValuesOf(DataType.NUMBER, [undefined, null]);
966
1076
 
967
1077
  ## Репозиторий
968
1078
 
969
- Выполняет операции чтения и записи документов определенной модели.
970
- Получить репозиторий можно методом `getRepository` экземпляра `DatabaseSchema`.
1079
+ Репозиторий выполняет операции чтения и записи данных определенной модели.
1080
+ Он выступает в роли посредника между бизнес-логикой приложения и базой данных.
971
1081
 
972
1082
  **Методы**
973
1083
 
974
- - `create(data, filter = undefined)` добавить новый документ
975
- - `replaceById(id, data, filter = undefined)` заменить весь документ
976
- - `replaceOrCreate(data, filter = undefined)` заменить или создать новый
977
- - `patchById(id, data, filter = undefined)` частично обновить документ
978
- - `patch(data, where = undefined)` обновить все документы или по условию
979
- - `find(filter = undefined)` найти все документы или по условию
980
- - `findOne(filter = undefined)` найти первый документ или по условию
981
- - `findById(id, filter = undefined)` найти документ по идентификатору
982
- - `delete(where = undefined)` удалить все документы или по условию
983
- - `deleteById(id)` удалить документ по идентификатору
984
- - `exists(id)` проверить существование по идентификатору
985
- - `count(where = undefined)` подсчет всех документов или по условию
1084
+ - [`create(data, filter = undefined)`](#repositorycreate) создать новый документ;
1085
+ - [`replaceById(id, data, filter = undefined)`](#repositoryreplacebyid) заменить документ полностью;
1086
+ - [`replaceOrCreate(data, filter = undefined)`](#repositoryreplaceorcreate) заменить или создать новый;
1087
+ - [`patchById(id, data, filter = undefined)`](#repositorypatchbyid) обновить документ частично;
1088
+ - [`patch(data, where = undefined)`](#repositorypatch) обновить все документы или по условию;
1089
+ - [`find(filter = undefined)`](#repositoryfind) найти все документы или по условию;
1090
+ - [`findOne(filter = undefined)`](#repositoryfindone) найти первый документ или по условию;
1091
+ - [`findById(id, filter = undefined)`](#repositoryfindbyid) найти документ по идентификатору;
1092
+ - [`delete(where = undefined)`](#repositorydelete) удалить все документы или по условию;
1093
+ - [`deleteById(id)`](#repositorydeletebyid) удалить документ по идентификатору;
1094
+ - [`exists(id)`](#repositoryexists) проверить существование по идентификатору;
1095
+ - [`count(where = undefined)`](#repositorycount) подсчет всех документов или по условию;
986
1096
 
987
1097
  **Аргументы**
988
1098
 
@@ -991,49 +1101,472 @@ emptyValuesService.setEmptyValuesOf(DataType.NUMBER, [undefined, null]);
991
1101
  - `where: object` условия фильтрации (см. [Фильтрация](#Фильтрация))
992
1102
  - `filter: object` параметры выборки (см. [Фильтрация](#Фильтрация))
993
1103
 
1104
+ **Получение репозитория**
1105
+
1106
+ Получить репозиторий можно с помощью метода `getRepository()` экземпляра
1107
+ `DatabaseSchema`. В качестве аргумента метод принимает название модели.
1108
+ Обязательным условием является наличие у модели определенного [источника данных](#источник-данных) (`datasource`), так как репозиторий напрямую взаимодействует
1109
+ с базой данных через указанный в источнике адаптер.
1110
+
1111
+ ```js
1112
+ // объявление источника
1113
+ dbs.defineDatasource({
1114
+ name: 'myDatasource',
1115
+ adapter: 'memory', // адаптер
1116
+ });
1117
+
1118
+ // объявление модели
1119
+ dbs.defineModel({
1120
+ name: 'myModel',
1121
+ datasource: 'myDatasource',
1122
+ // properties: { ... },
1123
+ // relations: { ... }
1124
+ });
1125
+
1126
+ // получение репозитория модели
1127
+ const modelRep = dbs.getRepository('myModel');
1128
+ ```
1129
+
1130
+ При первом вызове `getRepository('myModel')` будет создан и сохранен новый
1131
+ экземпляр репозитория. Все последующие вызовы с тем же названием модели будут
1132
+ возвращать уже существующий экземпляр.
1133
+
1134
+ ### repository.create
1135
+
1136
+ Создает новый документ в коллекции на основе переданных данных. Возвращает
1137
+ созданный документ с присвоенным идентификатором.
1138
+
1139
+ Сигнатура:
1140
+
1141
+ ```ts
1142
+ create(
1143
+ data: WithOptionalId<FlatData, IdName>,
1144
+ filter?: ItemFilterClause<FlatData>,
1145
+ ): Promise<FlatData>;
1146
+ ```
1147
+
994
1148
  **Примеры**
995
1149
 
996
- Получение репозитория по названию модели.
1150
+ Создание нового документа.
997
1151
 
998
1152
  ```js
999
- const countryRep = dbs.getRepository('country');
1153
+ const newProduct = await productRep.create({
1154
+ name: 'Laptop',
1155
+ price: 1200,
1156
+ });
1157
+ console.log(newProduct);
1158
+ // {
1159
+ // id: 1,
1160
+ // name: 'Laptop',
1161
+ // price: 1200,
1162
+ // }
1000
1163
  ```
1001
1164
 
1002
- Добавление нового документа в коллекцию.
1165
+ Создание документа с возвратом определенных полей.
1003
1166
 
1004
1167
  ```js
1005
- const res = await countryRep.create({
1006
- name: 'Russia',
1007
- population: 143400000,
1168
+ const product = await productRep.create(
1169
+ {name: 'Mouse', price: 25},
1170
+ {fields: ['id', 'name']},
1171
+ );
1172
+ console.log(product);
1173
+ // {
1174
+ // id: 2,
1175
+ // name: 'Mouse',
1176
+ // }
1177
+ ```
1178
+
1179
+ Создание документа с включением связанных данных в результат.
1180
+
1181
+ ```js
1182
+ // предполагается, что модель Product имеет связь "category"
1183
+ // (опция "include" влияет только на возвращаемый результат)
1184
+ const product = await productRep.create(
1185
+ {name: 'Keyboard', price: 75, categoryId: 10},
1186
+ {include: 'category'},
1187
+ );
1188
+ console.log(product);
1189
+ // {
1190
+ // id: 3,
1191
+ // name: 'Keyboard',
1192
+ // price: 75,
1193
+ // categoryId: 10,
1194
+ // category: {id: 10, name: 'Electronics'}
1195
+ // }
1196
+ ```
1197
+
1198
+ ### repository.replaceById
1199
+
1200
+ Полностью заменяет существующий документ по его идентификатору. Все предыдущие
1201
+ данные документа, кроме идентификатора, удаляются. Поля, которые не были
1202
+ переданы в `data`, будут отсутствовать в итоговом документе (если для них
1203
+ не задано значение по умолчанию).
1204
+
1205
+ Сигнатура:
1206
+
1207
+ ```ts
1208
+ replaceById(
1209
+ id: IdType,
1210
+ data: WithoutId<FlatData, IdName>,
1211
+ filter?: ItemFilterClause<FlatData>,
1212
+ ): Promise<FlatData>;
1213
+ ```
1214
+
1215
+ **Примеры**
1216
+
1217
+ Замена документа по идентификатору.
1218
+
1219
+ ```js
1220
+ // исходный документ
1221
+ // {
1222
+ // id: 1,
1223
+ // name: 'Laptop',
1224
+ // price: 1200,
1225
+ // inStock: true
1226
+ // }
1227
+
1228
+ const updatedProduct = await productRep.replaceById(1, {
1229
+ name: 'Laptop Pro',
1230
+ price: 1500,
1008
1231
  });
1232
+ console.log(updatedProduct);
1233
+ // {
1234
+ // id: 1,
1235
+ // name: 'Laptop Pro',
1236
+ // price: 1500
1237
+ // }
1238
+ // свойство "inStock" удалено
1239
+ ```
1240
+
1241
+ ### repository.replaceOrCreate
1242
+
1243
+ Заменяет существующий документ, если в переданных данных присутствует
1244
+ идентификатор, который уже существует в коллекции. В противном случае,
1245
+ если идентификатор не указан или не найден, создает новый документ.
1246
+
1247
+ Сигнатура:
1248
+
1249
+ ```ts
1250
+ replaceOrCreate(
1251
+ data: WithOptionalId<FlatData, IdName>,
1252
+ filter?: ItemFilterClause<FlatData>,
1253
+ ): Promise<FlatData>;
1254
+ ```
1255
+
1256
+ **Примеры**
1009
1257
 
1010
- console.log(res);
1258
+ Создание нового документа, если `id: 3` не существует.
1259
+
1260
+ ```js
1261
+ const product = await productRep.replaceOrCreate({
1262
+ id: 3,
1263
+ name: 'Keyboard',
1264
+ price: 75,
1265
+ });
1266
+ console.log(product);
1011
1267
  // {
1012
- // "id": 1,
1013
- // "name": "Russia",
1014
- // "population": 143400000,
1268
+ // id: 3,
1269
+ // name: 'Keyboard',
1270
+ // price: 75,
1015
1271
  // }
1016
1272
  ```
1017
1273
 
1018
- Поиск документа по идентификатору.
1274
+ Замена существующего документа с `id: 1`.
1019
1275
 
1020
1276
  ```js
1021
- const res = await countryRep.findById(1);
1277
+ const updatedProduct = await productRep.replaceOrCreate({
1278
+ id: 1,
1279
+ name: 'Laptop Pro',
1280
+ price: 1500,
1281
+ });
1282
+ console.log(updatedProduct);
1283
+ // {
1284
+ // id: 1,
1285
+ // name: 'Laptop Pro',
1286
+ // price: 1500,
1287
+ // }
1288
+ ```
1289
+
1290
+ ### repository.patchById
1291
+
1292
+ Частично обновляет существующий документ по его идентификатору, изменяя
1293
+ только переданные поля. Остальные поля документа остаются без изменений.
1022
1294
 
1023
- console.log(res);
1295
+ Сигнатура:
1296
+
1297
+ ```ts
1298
+ patchById(
1299
+ id: IdType,
1300
+ data: PartialWithoutId<FlatData, IdName>,
1301
+ filter?: ItemFilterClause<FlatData>,
1302
+ ): Promise<FlatData>;
1303
+ ```
1304
+
1305
+ **Примеры**
1306
+
1307
+ Частичное обновление документа по идентификатору.
1308
+
1309
+ ```js
1310
+ // исходный документ с id: 1
1311
+ // {
1312
+ // id: 1,
1313
+ // name: 'Laptop Pro',
1314
+ // price: 1500
1315
+ // }
1316
+
1317
+ const updatedProduct = await productRep.patchById(1, {
1318
+ price: 1450,
1319
+ });
1320
+ console.log(updatedProduct);
1024
1321
  // {
1025
- // "id": 1,
1026
- // "name": "Russia",
1027
- // "population": 143400000,
1322
+ // id: 1,
1323
+ // name: 'Laptop Pro',
1324
+ // price: 1450
1028
1325
  // }
1029
1326
  ```
1030
1327
 
1031
- Удаление документа по идентификатору.
1328
+ ### repository.patch
1329
+
1330
+ Частично обновляет один или несколько документов, соответствующих условиям
1331
+ `where`. Изменяются только переданные поля, остальные остаются без изменений.
1332
+ Возвращает количество обновленных документов. Если `where` не указан,
1333
+ обновляет все документы в коллекции.
1334
+
1335
+ Сигнатура:
1336
+
1337
+ ```ts
1338
+ patch(
1339
+ data: PartialWithoutId<FlatData, IdName>,
1340
+ where?: WhereClause<FlatData>,
1341
+ ): Promise<number>;
1342
+ ```
1343
+
1344
+ **Примеры**
1345
+
1346
+ Обновление документов по условию.
1347
+
1348
+ ```js
1349
+ // обновит все товары с ценой меньше 30
1350
+ const updatedCount = await productRep.patch(
1351
+ {inStock: false},
1352
+ {price: {lt: 30}},
1353
+ );
1354
+ ```
1355
+
1356
+ Обновление всех документов.
1357
+
1358
+ ```js
1359
+ // добавит или обновит поле updatedAt для всех документов
1360
+ const totalCount = await productRep.patch({
1361
+ updatedAt: new Date(),
1362
+ });
1363
+ ```
1364
+
1365
+ ### repository.find
1366
+
1367
+ Находит все документы, соответствующие условиям фильтрации, и возвращает их
1368
+ в виде массива. Если фильтр не указан, возвращает все документы коллекции.
1369
+
1370
+ Сигнатура:
1371
+
1372
+ ```ts
1373
+ find(filter?: FilterClause<FlatData>): Promise<FlatData[]>;
1374
+ ```
1375
+
1376
+ **Примеры**
1377
+
1378
+ Поиск всех документов.
1379
+
1380
+ ```js
1381
+ const allProducts = await productRep.find();
1382
+ ```
1383
+
1384
+ Поиск документов по условию `where`.
1385
+
1386
+ ```js
1387
+ const cheapProducts = await productRep.find({
1388
+ where: {price: {lt: 100}},
1389
+ });
1390
+ ```
1391
+
1392
+ Поиск с сортировкой и ограничением выборки.
1393
+
1394
+ ```js
1395
+ const latestProducts = await productRep.find({
1396
+ order: 'createdAt DESC',
1397
+ limit: 10,
1398
+ });
1399
+ ```
1400
+
1401
+ ### repository.findOne
1402
+
1403
+ Находит первый документ, соответствующий условиям фильтрации. Возвращает
1404
+ `undefined`, если документы не найдены.
1405
+
1406
+ Сигнатура:
1407
+
1408
+ ```ts
1409
+ findOne(
1410
+ filter?: FilterClause<FlatData>,
1411
+ ): Promise<FlatData | undefined>;
1412
+ ```
1413
+
1414
+ **Примеры**
1415
+
1416
+ Поиск одного документа по условию.
1417
+
1418
+ ```js
1419
+ const expensiveProduct = await productRep.findOne({
1420
+ where: {price: {gt: 1000}},
1421
+ order: 'price DESC',
1422
+ });
1423
+ ```
1424
+
1425
+ Обработка случая, когда документ не найден.
1032
1426
 
1033
1427
  ```js
1034
- const res = await countryRep.deleteById(1);
1428
+ const product = await productRep.findOne({
1429
+ where: {name: 'Non-existent Product'},
1430
+ });
1431
+ if (!product) {
1432
+ console.log('Product not found.');
1433
+ }
1434
+ ```
1435
+
1436
+ ### repository.findById
1437
+
1438
+ Находит один документ по его уникальному идентификатору. Если документ не
1439
+ найден, выбрасывается ошибка.
1035
1440
 
1036
- console.log(res); // true
1441
+ Сигнатура:
1442
+
1443
+ ```ts
1444
+ findById(
1445
+ id: IdType,
1446
+ filter?: ItemFilterClause<FlatData>,
1447
+ ): Promise<FlatData>;
1448
+ ```
1449
+
1450
+ **Примеры**
1451
+
1452
+ Поиск документа по `id`.
1453
+
1454
+ ```js
1455
+ try {
1456
+ const product = await productRep.findById(1);
1457
+ console.log(product);
1458
+ } catch (error) {
1459
+ console.error('Product with id 1 is not found.');
1460
+ }
1461
+ ```
1462
+
1463
+ Поиск документа с включением связанных данных.
1464
+
1465
+ ```js
1466
+ const product = await productRep.findById(1, {
1467
+ include: 'category',
1468
+ });
1469
+ ```
1470
+
1471
+ ### repository.delete
1472
+
1473
+ Удаляет один или несколько документов, соответствующих условиям `where`.
1474
+ Возвращает количество удаленных документов. Если `where` не указан, удаляет
1475
+ все документы в коллекции.
1476
+
1477
+ Сигнатура:
1478
+
1479
+ ```ts
1480
+ delete(where?: WhereClause<FlatData>): Promise<number>;
1481
+ ```
1482
+
1483
+ **Примеры**
1484
+
1485
+ Удаление документов по условию.
1486
+
1487
+ ```js
1488
+ const deletedCount = await productRep.delete({
1489
+ inStock: false,
1490
+ });
1491
+ ```
1492
+
1493
+ Удаление всех документов.
1494
+
1495
+ ```js
1496
+ const totalCount = await productRep.delete();
1497
+ ```
1498
+
1499
+ ### repository.deleteById
1500
+
1501
+ Удаляет один документ по его уникальному идентификатору. Возвращает `true`,
1502
+ если документ был найден и удален, в противном случае `false`.
1503
+
1504
+ Сигнатура:
1505
+
1506
+ ```ts
1507
+ deleteById(id: IdType): Promise<boolean>;
1508
+ ```
1509
+
1510
+ **Примеры**
1511
+
1512
+ Удаление документа по `id`.
1513
+
1514
+ ```js
1515
+ const wasDeleted = await productRep.deleteById(1);
1516
+ if (wasDeleted) {
1517
+ console.log('The document was deleted.');
1518
+ } else {
1519
+ console.log('No document found to delete.');
1520
+ }
1521
+ ```
1522
+
1523
+ ### repository.exists
1524
+
1525
+ Проверяет существование документа с указанным идентификатором. Возвращает
1526
+ `true`, если документ существует, иначе `false`.
1527
+
1528
+ Сигнатура:
1529
+
1530
+ ```ts
1531
+ exists(id: IdType): Promise<boolean>;
1532
+ ```
1533
+
1534
+ **Примеры**
1535
+
1536
+ Проверка существования документа по `id`.
1537
+
1538
+ ```js
1539
+ const productExists = await productRep.exists(1);
1540
+ if (productExists) {
1541
+ console.log('A document with id 1 exists.');
1542
+ }
1543
+ ```
1544
+
1545
+ ### repository.count
1546
+
1547
+ Подсчитывает количество документов, соответствующих условиям `where`. Если
1548
+ `where` не указан, возвращает общее количество документов в коллекции.
1549
+
1550
+ Сигнатура:
1551
+
1552
+ ```ts
1553
+ count(where?: WhereClause<FlatData>): Promise<number>;
1554
+ ```
1555
+
1556
+ **Примеры**
1557
+
1558
+ Подсчет документов по условию.
1559
+
1560
+ ```js
1561
+ const cheapCount = await productRep.count({
1562
+ price: {lt: 100},
1563
+ });
1564
+ ```
1565
+
1566
+ Подсчет всех документов.
1567
+
1568
+ ```js
1569
+ const totalCount = await productRep.count();
1037
1570
  ```
1038
1571
 
1039
1572
  ## Фильтрация
@@ -1488,6 +2021,28 @@ const res = await rep.find({
1488
2021
  в методах репозитория. Ниже приводится пример автоматического разрешения
1489
2022
  связи при использовании метода `findById`.
1490
2023
 
2024
+ ```
2025
+ Роль (role)
2026
+ ┌────────────────────┐
2027
+ │ id: 3 <──────────│────┐
2028
+ │ name: 'Manager' │ │
2029
+ └────────────────────┘ │
2030
+
2031
+ Пользователь (user) │
2032
+ ┌────────────────────────┐ │
2033
+ │ id: 1 │ │
2034
+ │ name: 'John Doe' │ │
2035
+ │ roleId: 3 ──────────│────┘
2036
+ │ cityId: 24 ──────────│────┐
2037
+ └────────────────────────┘ │
2038
+
2039
+ Город (city) │
2040
+ ┌────────────────────┐ │
2041
+ │ id: 24 <─────────│────┘
2042
+ │ name: 'Moscow' │
2043
+ └────────────────────┘
2044
+ ```
2045
+
1491
2046
  ```js
1492
2047
  // запрос документа коллекции "users",
1493
2048
  // включая связанные данные (role и city)
@@ -1519,8 +2074,11 @@ console.log(user);
1519
2074
  связи можно будет использовать в опции `include` методах репозитория.
1520
2075
 
1521
2076
  ```js
1522
- import {DataType} from '@e22m4u/js-repository';
1523
- import {RelationType} from '@e22m4u/js-repository';
2077
+ import {
2078
+ DataType,
2079
+ RelationType,
2080
+ DatabaseSchema,
2081
+ } from '@e22m4u/js-repository';
1524
2082
 
1525
2083
  dbs.defineModel({
1526
2084
  name: 'user',
@@ -1560,12 +2118,29 @@ dbs.defineModel({
1560
2118
  *i. Полиморфный режим позволяет динамически определять целевую модель
1561
2119
  по ее названию, которое хранит документ в свойстве-дискриминаторе.*
1562
2120
 
1563
- **Типы связи**
2121
+ ### Типы связей
2122
+
2123
+ - [Belongs To](#belongs-to)
2124
+ Текущая модель ссылается на целевую по идентификатору.
2125
+ `type: "belongsTo"` или `type: RelationType.BELONGS_TO`
2126
+
2127
+ - [Has One](#has-one)
2128
+ Обратная сторона `belongsTo` по принципу *"один к одному"*.
2129
+ `type: "hasOne"` или `type: RelationType.HAS_ONE`
1564
2130
 
1565
- - `belongsTo` - текущая модель ссылается на целевую по идентификатору;
1566
- - `hasOne` - обратная сторона `belongsTo` по принципу "один к одному";
1567
- - `hasMany` - обратная сторона `belongsTo` по принципу "один ко многим";
1568
- - `referencesMany` - модель ссылается через массив идентификаторов;
2131
+ - [Has Many](#has-many)
2132
+ Обратная сторона `belongsTo` по принципу *"один ко многим"*.
2133
+ `type: "hasMany"` или `type: RelationType.HAS_MANY`
2134
+
2135
+ - [References Many](#references-many)
2136
+ Текущая модель ссылается на целевую через массив идентификаторов.
2137
+ `type: "referencesMany"` или `type: RelationType.REFERENCES_MANY`
2138
+
2139
+ Полиморфные версии:
2140
+
2141
+ - [Belongs To (полиморфная)](#belongs-to-полиморфная-версия)
2142
+ - [Has One (полиморфная)](#has-one-полиморфная-версия)
2143
+ - [Has Many (полиморфная)](#has-many-полиморфная-версия)
1569
2144
 
1570
2145
  Параметр `type` в определении связи принимает строку с названием типа. Чтобы исключить опечатку, рекомендуется использовать константы объекта `RelationType`
1571
2146
  указанные ниже.
@@ -1575,9 +2150,25 @@ dbs.defineModel({
1575
2150
  - `RelationType.HAS_MANY`
1576
2151
  - `RelationType.REFERENCES_MANY`
1577
2152
 
1578
- **Примеры**
2153
+ #### Belongs To
2154
+
2155
+ Текущая модель ссылается на целевую по идентификатору.
2156
+
2157
+ ```
2158
+ Текущая (user) Целевая (role)
2159
+ ┌─────────────────────────┐ ┌─────────────────────────┐
2160
+ │ id: 1 │ ┌───│─> id: 5 │
2161
+ │ roleId: 5 ───────────│───┤ │ ... │
2162
+ │ ... │ │ └─────────────────────────┘
2163
+ └─────────────────────────┘ │
2164
+ ┌─────────────────────────┐ │
2165
+ │ id: 2 │ │
2166
+ │ roleId: 5 ───────────│───┘
2167
+ │ ... │
2168
+ └─────────────────────────┘
2169
+ ```
1579
2170
 
1580
- Объявление связи `belongsTo`.
2171
+ Определение связи:
1581
2172
 
1582
2173
  ```js
1583
2174
  dbs.defineModel({
@@ -1595,24 +2186,191 @@ dbs.defineModel({
1595
2186
  });
1596
2187
  ```
1597
2188
 
1598
- Объявление связи `hasMany`.
2189
+ Пример:
2190
+
2191
+ ```js
2192
+ import {
2193
+ DataType,
2194
+ RelationType,
2195
+ DatabaseSchema,
2196
+ } from '@e22m4u/js-repository';
2197
+
2198
+ const dbs = new DatabaseSchema();
2199
+
2200
+ // источник данных
2201
+ dbs.defineDatasource({
2202
+ name: 'myDb',
2203
+ adapter: 'memory',
2204
+ });
2205
+
2206
+ // модель роли
2207
+ dbs.defineModel({
2208
+ name: 'role',
2209
+ datasource: 'myDb',
2210
+ properties: {
2211
+ name: DataType.STRING,
2212
+ },
2213
+ });
2214
+
2215
+ // модель пользователя
2216
+ dbs.defineModel({
2217
+ name: 'user',
2218
+ datasource: 'myDb',
2219
+ properties: {
2220
+ name: DataType.STRING,
2221
+ roleId: DataType.NUMBER, // не обязательно
2222
+ },
2223
+ relations: {
2224
+ role: {
2225
+ type: RelationType.BELONGS_TO,
2226
+ model: 'role',
2227
+ foreignKey: 'roleId', // не обязательно
2228
+ },
2229
+ },
2230
+ });
2231
+
2232
+ // создание роли
2233
+ const roleRep = dbs.getRepository('role');
2234
+ const role = await roleRep.create({
2235
+ id: 5,
2236
+ name: 'Manager',
2237
+ });
2238
+ console.log(role);
2239
+ // {
2240
+ // id: 5,
2241
+ // name: 'manager'
2242
+ // }
2243
+
2244
+ // создание пользователя
2245
+ const userRep = dbs.getRepository('user');
2246
+ const user = await userRep.create({
2247
+ id: 1,
2248
+ name: 'John Doe',
2249
+ roleId: role.id,
2250
+ });
2251
+ console.log(user);
2252
+ // {
2253
+ // id: 1,
2254
+ // name: 'John Doe',
2255
+ // roleId: 5
2256
+ // }
2257
+
2258
+ // извлечение пользователя и связанной роли (опция "include")
2259
+ const userWithRole = await userRep.findById(user.id, {include: 'role'});
2260
+ console.log(userWithRole);
2261
+ // {
2262
+ // id: 1,
2263
+ // name: 'John Doe',
2264
+ // roleId: 5,
2265
+ // role: {
2266
+ // id: 5,
2267
+ // name: 'Manager'
2268
+ // }
2269
+ // }
2270
+ ```
2271
+
2272
+ #### Has One
2273
+
2274
+ Обратная сторона `belongsTo` по принципу *"один к одному"*.
2275
+
2276
+ ```
2277
+ Текущая (profile) Целевая (user)
2278
+ ┌─────────────────────────┐ ┌─────────────────────────┐
2279
+ │ id: 5 <──────────────│───┐ │ id: 1 │
2280
+ │ ... │ └───│── profileId: 5 │
2281
+ └─────────────────────────┘ │ ... │
2282
+ └─────────────────────────┘
2283
+ ```
2284
+
2285
+ Определение связи:
2286
+
2287
+ ```js
2288
+ // dbs.defineModel({
2289
+ // name: 'user',
2290
+ // relations: {
2291
+ // profile: {
2292
+ // type: RelationType.BELONGS_TO,
2293
+ // model: 'profile',
2294
+ // },
2295
+ // },
2296
+ // });
2297
+
2298
+ dbs.defineModel({
2299
+ name: 'profile',
2300
+ relations: {
2301
+ user: { // название связи
2302
+ type: RelationType.HAS_ONE, // целевая модель ссылается на текущую
2303
+ model: 'user', // название целевой модели
2304
+ foreignKey: 'profileId', // внешний ключ из целевой модели на текущую
2305
+ },
2306
+ },
2307
+ });
2308
+ ```
2309
+
2310
+ #### Has Many
2311
+
2312
+ Обратная сторона `belongsTo` по принципу *"один ко многим"*.
2313
+
2314
+ ```
2315
+ Текущая (role) Целевая (user)
2316
+ ┌─────────────────────────┐ ┌─────────────────────────┐
2317
+ │ id: 5 <──────────────│───┐ │ id: 1 │
2318
+ │ ... │ ├───│── roleId: 5 │
2319
+ └─────────────────────────┘ │ │ ... │
2320
+ │ └─────────────────────────┘
2321
+ │ ┌─────────────────────────┐
2322
+ │ │ id: 2 │
2323
+ └───│── roleId: 5 │
2324
+ │ ... │
2325
+ └─────────────────────────┘
2326
+ ```
2327
+
2328
+ Определение связи:
1599
2329
 
1600
2330
  ```js
2331
+ // dbs.defineModel({
2332
+ // name: 'user',
2333
+ // relations: {
2334
+ // role: {
2335
+ // type: RelationType.BELONGS_TO,
2336
+ // model: 'role',
2337
+ // },
2338
+ // },
2339
+ // });
2340
+
1601
2341
  dbs.defineModel({
1602
2342
  name: 'role',
1603
2343
  relations: {
1604
2344
  users: { // название связи
1605
2345
  type: RelationType.HAS_MANY, // целевая модель ссылается на текущую
1606
2346
  model: 'user', // название целевой модели
1607
- foreignKey: 'roleId', // внешний ключ из целевой модели на текущую
2347
+ foreignKey: 'roleId', // внешний ключ целевой модели
1608
2348
  },
1609
2349
  },
1610
2350
  });
1611
2351
  ```
1612
2352
 
1613
- Объявление связи `referencesMany`.
2353
+ #### References Many
2354
+
2355
+ Текущая модель ссылается на целевую через массив идентификаторов.
2356
+
2357
+ ```
2358
+ Текущая (article) Целевая (category)
2359
+ ┌─────────────────────────┐ ┌─────────────────────────┐
2360
+ │ id: 1 │ ┌───│─> id: 5 │
2361
+ │ categoryIds: [5, 6] ──│───┤ │ ... │
2362
+ │ ... │ │ └─────────────────────────┘
2363
+ └─────────────────────────┘ │ ┌─────────────────────────┐
2364
+ └───│─> id: 6 │
2365
+ │ ... │
2366
+ └─────────────────────────┘
2367
+ ```
2368
+
2369
+ Определение связи:
1614
2370
 
1615
2371
  ```js
2372
+ // dbs.defineModel({name: 'category', ...
2373
+
1616
2374
  dbs.defineModel({
1617
2375
  name: 'article',
1618
2376
  relations: {
@@ -1628,7 +2386,27 @@ dbs.defineModel({
1628
2386
  });
1629
2387
  ```
1630
2388
 
1631
- Полиморфная версия `belongsTo`.
2389
+ #### Belongs To (полиморфная версия)
2390
+
2391
+ Текущая модель ссылается на целевую по идентификатору. Название целевой модели
2392
+ определяется свойством-дискриминатором.
2393
+
2394
+ ```
2395
+ Текущая (file) ┌──────> Целевая 1 (letter)
2396
+ ┌─────────────────────────────┐ │ ┌─────────────────────────┐
2397
+ │ id: 1 │ │ ┌──│─> id: 10 │
2398
+ │ referenceType: 'letter' ─│──┘ │ │ ... │
2399
+ │ referenceId: 10 ─────────│────┘ └─────────────────────────┘
2400
+ └─────────────────────────────┘
2401
+ ┌──────> Целевая 2 (user)
2402
+ ┌─────────────────────────────┐ │ ┌─────────────────────────┐
2403
+ │ id: 2 │ │ ┌──│─> id: 5 │
2404
+ │ referenceType: 'user' ───│──┘ │ │ ... │
2405
+ │ referenceId: 5 ──────────│────┘ └─────────────────────────┘
2406
+ └─────────────────────────────┘
2407
+ ```
2408
+
2409
+ Определение связи:
1632
2410
 
1633
2411
  ```js
1634
2412
  dbs.defineModel({
@@ -1647,7 +2425,7 @@ dbs.defineModel({
1647
2425
  });
1648
2426
  ```
1649
2427
 
1650
- Полиморфная версия `belongsTo` с указанием свойств.
2428
+ Определение связи с указанием свойств:
1651
2429
 
1652
2430
  ```js
1653
2431
  dbs.defineModel({
@@ -1663,9 +2441,104 @@ dbs.defineModel({
1663
2441
  });
1664
2442
  ```
1665
2443
 
1666
- Полиморфная версия `hasMany` с указанием названия связи целевой модели.
2444
+ #### Has One (полиморфная версия)
2445
+
2446
+ Обратная сторона полиморфная связи `belongsTo` по принципу *"один к одному"*.
2447
+
2448
+ ```
2449
+ Текущая (company) <───────┐ Целевая (license)
2450
+ ┌─────────────────────────┐ │ ┌─────────────────────────┐
2451
+ │ id: 10 <─────────────│──┐ │ │ id: 1 │
2452
+ │ ... │ │ └──│── ownerType: 'company' │
2453
+ └─────────────────────────┘ └────│── ownerId: 10 │
2454
+ └─────────────────────────┘
2455
+ ```
2456
+
2457
+ Определение связи с указанием названия связи целевой модели:
2458
+
2459
+ ```js
2460
+ // dbs.defineModel({
2461
+ // name: 'license',
2462
+ // relations: {
2463
+ // owner: {
2464
+ // type: RelationType.BELONGS_TO,
2465
+ // polymorphic: true,
2466
+ // },
2467
+ // },
2468
+ // });
2469
+
2470
+ dbs.defineModel({
2471
+ name: 'company',
2472
+ relations: {
2473
+ license: { // название связи
2474
+ type: RelationType.HAS_ONE, // целевая модель ссылается на текущую
2475
+ model: 'license', // название целевой модели
2476
+ polymorphic: 'owner', // название полиморфной связи целевой модели
2477
+ },
2478
+ },
2479
+ });
2480
+ ```
2481
+
2482
+ Определение связи с указанием свойств целевой модели:
2483
+
2484
+ ```js
2485
+ // dbs.defineModel({
2486
+ // name: 'license',
2487
+ // relations: {
2488
+ // owner: {
2489
+ // type: RelationType.BELONGS_TO,
2490
+ // polymorphic: true,
2491
+ // foreignKey: 'ownerId',
2492
+ // discriminator: 'ownerType',
2493
+ // },
2494
+ // },
2495
+ // });
2496
+
2497
+ dbs.defineModel({
2498
+ name: 'company',
2499
+ relations: {
2500
+ license: { // название связи
2501
+ type: RelationType.HAS_ONE, // целевая модель ссылается на текущую
2502
+ model: 'license', // название целевой модели
2503
+ polymorphic: true, // название текущей модели находится в дискриминаторе
2504
+ foreignKey: 'ownerId', // свойство целевой модели для идентификатора
2505
+ discriminator: 'ownerType', // свойство целевой модели для названия текущей
2506
+ },
2507
+ },
2508
+ });
2509
+ ```
2510
+
2511
+ #### Has Many (полиморфная версия)
2512
+
2513
+ Обратная сторона полиморфная связи `belongsTo` по принципу *"один ко многим"*.
2514
+
2515
+ ```
2516
+ Текущая (letter) <─────────┐ Целевая (file)
2517
+ ┌──────────────────────────┐ │ ┌────────────────────────────┐
2518
+ │ id: 10 <──────────────│──┐ │ │ id: 1 │
2519
+ │ ... │ │ ├──│── referenceType: 'letter' │
2520
+ └──────────────────────────┘ ├─│──│── referenceId: 10 │
2521
+ │ │ └────────────────────────────┘
2522
+ │ │ ┌────────────────────────────┐
2523
+ │ │ │ id: 2 │
2524
+ │ └──│── referenceType: 'letter' │
2525
+ └────│── referenceId: 10 │
2526
+ └────────────────────────────┘
2527
+ ```
2528
+
2529
+ Определение связи с указанием названия связи целевой модели:
1667
2530
 
1668
2531
  ```js
2532
+ // dbs.defineModel({
2533
+ // name: 'file',
2534
+ // relations: {
2535
+ // reference: {
2536
+ // type: RelationType.BELONGS_TO,
2537
+ // polymorphic: true,
2538
+ // },
2539
+ // },
2540
+ // });
2541
+
1669
2542
  dbs.defineModel({
1670
2543
  name: 'letter',
1671
2544
  relations: {
@@ -1678,9 +2551,21 @@ dbs.defineModel({
1678
2551
  });
1679
2552
  ```
1680
2553
 
1681
- Полиморфная версия `hasMany` с указанием свойств целевой модели.
2554
+ Определение связи с указанием свойств целевой модели:
1682
2555
 
1683
2556
  ```js
2557
+ // dbs.defineModel({
2558
+ // name: 'file',
2559
+ // relations: {
2560
+ // reference: {
2561
+ // type: RelationType.BELONGS_TO,
2562
+ // polymorphic: true,
2563
+ // foreignKey: 'referenceId',
2564
+ // discriminator: 'referenceType',
2565
+ // },
2566
+ // },
2567
+ // });
2568
+
1684
2569
  dbs.defineModel({
1685
2570
  name: 'letter',
1686
2571
  relations: {
@@ -1733,7 +2618,7 @@ class MyRepository extends Repository {
1733
2618
  // dbs.defineDatasource ...
1734
2619
  // dbs.defineModel ...
1735
2620
 
1736
- dbs.get(RepositoryRegistry).setRepositoryCtor(MyRepository);
2621
+ dbs.getService(RepositoryRegistry).setRepositoryCtor(MyRepository);
1737
2622
  const rep = dbs.getRepository('model');
1738
2623
  console.log(rep instanceof MyRepository); // true
1739
2624
  ```
@@ -1752,7 +2637,6 @@ import {DatabaseSchema} from '@e22m4u/js-repository';
1752
2637
 
1753
2638
  // const dbs = new DatabaseSchema();
1754
2639
  // dbs.defineDatasource ...
1755
- // dbs.defineModel ...
1756
2640
 
1757
2641
  // определение модели "city"
1758
2642
  dbs.defineModel({
@@ -1762,12 +2646,6 @@ dbs.defineModel({
1762
2646
  name: DataType.STRING,
1763
2647
  timeZone: DataType.STRING,
1764
2648
  },
1765
- relations: {
1766
- country: {
1767
- type: RelationType.BELONGS_TO,
1768
- model: 'country',
1769
- },
1770
- },
1771
2649
  });
1772
2650
 
1773
2651
  // определение интерфейса "city"
@@ -1775,15 +2653,26 @@ interface City {
1775
2653
  id: number;
1776
2654
  name?: string;
1777
2655
  timeZone?: string;
1778
- countryId?: number;
1779
- country?: Country;
1780
2656
  }
1781
2657
 
1782
- // получаем репозиторий по названию модели
1783
- // указывая ее тип и тип идентификатора
1784
- const cityRep = dbs.getRepository<City, number>('city');
2658
+ // при получении репозитория нужной модели
2659
+ // можно указать тип документов
2660
+ const cityRep = dbs.getRepository<City>('city');
2661
+
2662
+ // теперь, методы репозитория возвращают
2663
+ // тип City вместо Record<string, unknown>
2664
+ const city: City = await cityRep.create({
2665
+ name: 'Moscow',
2666
+ timeZone: 'Europe/Moscow',
2667
+ });
1785
2668
  ```
1786
2669
 
2670
+ Для определения моделей с помощью TypeScript классов,
2671
+ рекомендуется использовать специальную версию данного модуля
2672
+ [@e22m4u/ts-repository](https://www.npmjs.com/package/@e22m4u/ts-repository),
2673
+ поставляемую с набором TypeScript декораторов и дополнительных
2674
+ инструментов для работы в TypeScript окружении.
2675
+
1787
2676
  ## Тесты
1788
2677
 
1789
2678
  ```bash