@e22m4u/js-repository 0.0.41 → 0.1.0

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.
Files changed (113) hide show
  1. package/.eslintignore +1 -0
  2. package/.husky/pre-commit +1 -0
  3. package/README.md +516 -526
  4. package/docs/.nojekyll +1 -0
  5. package/docs/assets/highlight.css +99 -0
  6. package/docs/assets/main.js +59 -0
  7. package/docs/assets/navigation.js +1 -0
  8. package/docs/assets/search.js +1 -0
  9. package/docs/assets/style.css +1394 -0
  10. package/docs/classes/Adapter.html +38 -0
  11. package/docs/classes/AdapterLoader.html +16 -0
  12. package/docs/classes/AdapterRegistry.html +16 -0
  13. package/docs/classes/BelongsToResolver.html +18 -0
  14. package/docs/classes/DatasourceDefinitionValidator.html +16 -0
  15. package/docs/classes/DefinitionRegistry.html +26 -0
  16. package/docs/classes/FieldsClauseTool.html +20 -0
  17. package/docs/classes/HasManyResolver.html +20 -0
  18. package/docs/classes/HasOneResolver.html +20 -0
  19. package/docs/classes/IncludeClauseTool.html +24 -0
  20. package/docs/classes/InvalidArgumentError.html +14 -0
  21. package/docs/classes/InvalidOperatorValueError.html +14 -0
  22. package/docs/classes/ModelDataSanitizer.html +16 -0
  23. package/docs/classes/ModelDataValidator.html +18 -0
  24. package/docs/classes/ModelDefinitionUtils.html +46 -0
  25. package/docs/classes/ModelDefinitionValidator.html +16 -0
  26. package/docs/classes/NotImplementedError.html +14 -0
  27. package/docs/classes/OperatorClauseTool.html +72 -0
  28. package/docs/classes/OrderClauseTool.html +20 -0
  29. package/docs/classes/PrimaryKeysDefinitionValidator.html +16 -0
  30. package/docs/classes/PropertiesDefinitionValidator.html +16 -0
  31. package/docs/classes/ReferencesManyResolver.html +16 -0
  32. package/docs/classes/RelationsDefinitionValidator.html +16 -0
  33. package/docs/classes/Repository.html +44 -0
  34. package/docs/classes/RepositoryRegistry.html +18 -0
  35. package/docs/classes/Schema.html +20 -0
  36. package/docs/classes/SliceClauseTool.html +20 -0
  37. package/docs/classes/WhereClauseTool.html +18 -0
  38. package/docs/enums/DataType.html +8 -0
  39. package/docs/enums/RelationType.html +6 -0
  40. package/docs/functions/capitalize.html +2 -0
  41. package/docs/functions/cloneDeep.html +2 -0
  42. package/docs/functions/excludeObjectKeys.html +2 -0
  43. package/docs/functions/getCtorName.html +2 -0
  44. package/docs/functions/getValueByPath.html +2 -0
  45. package/docs/functions/isCtor.html +2 -0
  46. package/docs/functions/isPureObject.html +2 -0
  47. package/docs/functions/selectObjectKeys.html +2 -0
  48. package/docs/functions/singularize.html +2 -0
  49. package/docs/functions/stringToRegexp.html +2 -0
  50. package/docs/index.html +284 -0
  51. package/docs/interfaces/AndClause.html +5 -0
  52. package/docs/interfaces/OrClause.html +5 -0
  53. package/docs/modules.html +80 -0
  54. package/docs/types/AnyObject.html +2 -0
  55. package/docs/types/BelongsToDefinition.html +6 -0
  56. package/docs/types/DEFAULT_PRIMARY_KEY_PROPERTY_NAME.html +2 -0
  57. package/docs/types/DatasourceDefinition.html +2 -0
  58. package/docs/types/FieldsClause.html +4 -0
  59. package/docs/types/FilterClause.html +2 -0
  60. package/docs/types/Flatten.html +1 -0
  61. package/docs/types/FullPropertyDefinition.html +2 -0
  62. package/docs/types/HasManyDefinition.html +4 -0
  63. package/docs/types/HasOneDefinition.html +4 -0
  64. package/docs/types/Identity.html +2 -0
  65. package/docs/types/IncludeClause.html +14 -0
  66. package/docs/types/ItemFilterClause.html +2 -0
  67. package/docs/types/ModelData.html +2 -0
  68. package/docs/types/ModelDefinition.html +2 -0
  69. package/docs/types/ModelId.html +2 -0
  70. package/docs/types/NestedIncludeClause.html +10 -0
  71. package/docs/types/NormalizedFieldsClause.html +4 -0
  72. package/docs/types/NormalizedIncludeClause.html +6 -0
  73. package/docs/types/OperatorClause.html +4 -0
  74. package/docs/types/OptionalUnlessRequiredId.html +2 -0
  75. package/docs/types/OrderClause.html +4 -0
  76. package/docs/types/PartialBy.html +2 -0
  77. package/docs/types/PartialWithoutId.html +2 -0
  78. package/docs/types/PolyBelongsToDefinition.html +6 -0
  79. package/docs/types/PolyHasManyDefinitionWithTargetKeys.html +4 -0
  80. package/docs/types/PolyHasManyDefinitionWithTargetRelationName.html +4 -0
  81. package/docs/types/PolyHasOneDefinitionWithTargetKeys.html +4 -0
  82. package/docs/types/PolyHasOneDefinitionWithTargetRelationName.html +4 -0
  83. package/docs/types/PropertiesClause.html +4 -0
  84. package/docs/types/PropertyDefinition.html +2 -0
  85. package/docs/types/PropertyDefinitionMap.html +2 -0
  86. package/docs/types/ReferencesManyDefinition.html +6 -0
  87. package/docs/types/RelationDefinition.html +4 -0
  88. package/docs/types/RelationDefinitionMap.html +2 -0
  89. package/docs/types/WhereClause.html +4 -0
  90. package/docs/types/WithoutId.html +2 -0
  91. package/package.json +14 -12
  92. package/src/adapter/adapter.d.ts +13 -0
  93. package/src/adapter/adapter.js +15 -0
  94. package/src/adapter/adapter.spec.js +8 -0
  95. package/src/adapter/builtin/memory-adapter.d.ts +13 -0
  96. package/src/adapter/builtin/memory-adapter.js +30 -0
  97. package/src/adapter/builtin/memory-adapter.spec.js +660 -8
  98. package/src/adapter/decorator/data-sanitizing-decorator.js +6 -0
  99. package/src/adapter/decorator/data-sanitizing-decorator.spec.js +13 -0
  100. package/src/adapter/decorator/data-validation-decorator.js +6 -0
  101. package/src/adapter/decorator/data-validation-decorator.spec.js +13 -0
  102. package/src/adapter/decorator/default-values-decorator.js +6 -0
  103. package/src/adapter/decorator/default-values-decorator.spec.js +16 -0
  104. package/src/adapter/decorator/fields-filtering-decorator.js +13 -0
  105. package/src/adapter/decorator/fields-filtering-decorator.spec.js +17 -0
  106. package/src/adapter/decorator/inclusion-decorator.js +13 -0
  107. package/src/adapter/decorator/inclusion-decorator.spec.js +17 -0
  108. package/src/definition/model/relations/relation-definition.d.ts +11 -29
  109. package/src/definition/model/relations/relations-definition-validator.js +4 -4
  110. package/src/definition/model/relations/relations-definition-validator.spec.js +18 -18
  111. package/src/filter/filter-clause.d.ts +5 -4
  112. package/src/repository/repository.js +2 -6
  113. package/typedoc.json +4 -0
package/README.md CHANGED
@@ -1,6 +1,23 @@
1
1
  ## @e22m4u/js-repository
2
2
 
3
- Абстракция для работы с базами данных для Node.js
3
+ Модуль для работы с базами данных для Node.js
4
+
5
+ [API](https://e22m4u.github.io/js-repository/modules.html)
6
+
7
+ - [Установка](#Установка)
8
+ - [Описание](#Описание)
9
+ - [Пример](#Пример)
10
+ - [Схема](#Схема)
11
+ - [Источник данных](#Источник-данных)
12
+ - [Модель](#Модель)
13
+ - [Свойства](#Свойства)
14
+ - [Репозиторий](#Репозиторий)
15
+ - [Фильтрация](#Фильтрация)
16
+ - [Связи](#Связи)
17
+ - [Расширение](#Расширение)
18
+ - [TypeScript](#TypeScript)
19
+ - [Тесты](#Тесты)
20
+ - [Лицензия](#Лицензия)
4
21
 
5
22
  ## Установка
6
23
 
@@ -8,586 +25,510 @@
8
25
  npm install @e22m4u/js-repository
9
26
  ```
10
27
 
11
- Опционально устанавливаем адаптер. Например, если используется
12
- *MongoDB*, то для подключения потребуется добавить
13
- [адаптер mongodb](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter)
14
- как отдельную зависимость.
28
+ Опционально устанавливаем адаптер.
15
29
 
16
- ```bash
17
- npm install @e22m4u/js-repository-mongodb-adapter
18
- ```
30
+ | | описание |
31
+ |-----------|--------------------------------------------------------------------------------------------------------------------------------|
32
+ | `memory` | виртуальная база в памяти процесса (не требует установки) |
33
+ | `mongodb` | MongoDB - система управления NoSQL базами (*[установка](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
19
34
 
20
- **Список доступных адаптеров:**
35
+ ## Описание
21
36
 
22
- | адаптер | описание |
23
- |-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
24
- | `memory` | виртуальная база в памяти процесса (для разработки и тестирования) |
25
- | `mongodb` | MongoDB - система управления NoSQL базами (*[требует установки](https://www.npmjs.com/package/@e22m4u/js-repository-mongodb-adapter))* |
37
+ Модуль позволяет объединить несколько баз данных в единую абстракцию «Схема».
26
38
 
27
- ## Концепция
28
-
29
- Модуль позволяет спроектировать систему связанных данных, доступ к которым
30
- осуществляется посредством репозиториев. Каждый репозиторий имеет собственную
31
- модель данных, которая описывает структуру определенной коллекции в базе,
32
- а так же определяет связи к другим коллекциям.
39
+ - *Источник данных* - определяет способ подключения к базе
40
+ - *Модель* - описывает структуру документа и связи к другим моделям
41
+ - *Репозиторий* - выполняет операции чтения и записи документов модели
33
42
 
34
43
  ```mermaid
35
- flowchart LR
36
-
37
- A[Datasource]-->B[Data Model]-->С[Repository];
44
+ flowchart TD
45
+
46
+ A[Схема]
47
+ subgraph Базы данных
48
+ B[Источник данных 1]
49
+ C[Источник данных 2]
50
+ end
51
+ A-->B
52
+ A-->C
53
+
54
+ subgraph Коллекции
55
+ D[Модель A]
56
+ E[Модель Б]
57
+ F[Модель В]
58
+ G[Модель Г]
59
+ end
60
+ B-->D
61
+ B-->E
62
+ C-->F
63
+ C-->G
64
+
65
+ H[Репозиторий A]
66
+ I[Репозиторий Б]
67
+ J[Репозиторий В]
68
+ K[Репозиторий Г]
69
+ D-->H
70
+ E-->I
71
+ F-->J
72
+ G-->K
38
73
  ```
39
74
 
40
- ## Использование
75
+ ## Пример
41
76
 
42
- Определения источников и моделей хранятся в экземпляре класса `Schema`,
43
- и первым шагом будет создание данного экземпляра.
77
+ Определение источника данных, модели и добавление нового документа в коллекцию.
44
78
 
45
79
  ```js
46
80
  import {Schema} from '@e22m4u/js-repository';
81
+ import {DataType} from '@e22m4u/js-repository';
47
82
 
83
+ // создание экземпляра Schema
48
84
  const schema = new Schema();
85
+
86
+ // определение источника "myMemory"
87
+ schema.defineDatasource({
88
+ name: 'myMemory', // название нового источника
89
+ adapter: 'memory', // выбранный адаптер
90
+ });
91
+
92
+ // определение модели "country"
93
+ schema.defineModel({
94
+ name: 'country', // название новой модели
95
+ datasource: 'myMemory', // выбранный источник
96
+ properties: { // свойства модели
97
+ name: DataType.STRING, // тип "string"
98
+ population: DataType.NUMBER, // тип "number"
99
+ },
100
+ })
101
+
102
+ // получение репозитория для модели "country"
103
+ const countryRep = schema.getRepository('country');
104
+
105
+ // добавление нового документа в коллекцию "country"
106
+ const country = await countryRep.create({
107
+ name: 'Russia',
108
+ population: 143400000,
109
+ });
110
+
111
+ // вывод результата
112
+ console.log(country);
113
+ // {
114
+ // "id": 1,
115
+ // "name": "Russia",
116
+ // "population": 143400000,
117
+ // }
49
118
  ```
50
119
 
51
- Интерфейс `Schema` содержит три основных метода:
120
+ ## Схема
121
+
122
+ Экземпляр класса `Schema` хранит определения источников данных и моделей.
123
+
124
+ **Методы**
52
125
 
53
126
  - `defineDatasource(datasourceDef: object): this` - добавить источник
54
127
  - `defineModel(modelDef: object): this` - добавить модель
55
128
  - `getRepository(modelName: string): Repository` - получить репозиторий
56
129
 
57
- ### Источник данных
130
+ **Примеры**
58
131
 
59
- Источник описывает способ подключения к базе и используемый адаптер.
60
- Если адаптер имеет настройки, то они передаются вместе с объектом
61
- определения источника методом `defineDatasource`, как это показано
62
- ниже.
132
+ Импорт класса и создание экземпляра схемы.
63
133
 
64
134
  ```js
65
- schema.defineDatasource({
66
- name: 'myMongo', // название нового источника
67
- adapter: 'mongodb', // название выбранного адаптера
68
- // настройки адаптера mongodb
69
- host: '127.0.0.1',
70
- port: 27017,
71
- database: 'data'
72
- });
73
- ```
74
-
75
- **Параметры источника:**
135
+ import {Schema} from '@e22m4u/js-repository';
76
136
 
77
- - `name: string` уникальное название
78
- - `adapter: string` выбранный адаптер
137
+ const schema = new Schema();
138
+ ```
79
139
 
80
- При желании можно использовать встроенный адаптер `memory`, который хранит
81
- данные в памяти процесса. У него нет специальных настроек, и он отлично
82
- подходит для тестов и прототипирования.
140
+ Определение нового источника.
83
141
 
84
142
  ```js
85
143
  schema.defineDatasource({
86
- name: 'myMemory', // название источника
144
+ name: 'myMemory', // название нового источника
87
145
  adapter: 'memory', // выбранный адаптер
88
146
  });
89
147
  ```
90
148
 
91
- ### Модель данных
92
-
93
- Когда источники определены, можно перейти к описанию моделей данных.
94
- Модель может определять как структуру какого-либо объекта,
95
- так и являться отражением реальной коллекции базы.
96
-
97
- Представьте себе коллекцию торговых точек, у каждой из которых имеются
98
- координаты `lat` и `lng`. Мы можем заранее определить модель для
99
- объекта координат методом `defineModel` и использовать ее в других
100
- коллекциях.
149
+ Определение новой модели.
101
150
 
102
151
  ```js
103
152
  schema.defineModel({
104
- name: 'latLng', // название новой модели
105
- properties: { // поля модели
106
- lat: DataType.NUMBER, // поле широты
107
- lng: DataType.NUMBER, // поле долготы
153
+ name: 'product', // название новой модели
154
+ datasource: 'myMemory', // выбранный источник
155
+ properties: { // свойства модели
156
+ name: DataType.STRING,
157
+ weight: DataType.NUMBER,
108
158
  },
109
159
  });
110
160
  ```
111
161
 
112
- **Параметры модели:**
113
-
114
- - `name: string` уникальное название (обязательно)
115
- - `datasource: string` выбранный источник данных
116
- - `properties: object` определения полей модели
117
- - `relations: object` определения связей модели
118
-
119
- Параметр `properties` принимает объект, ключи которого являются именами
120
- полей, а значением тип поля или объект с дополнительными параметрами.
162
+ Получение репозитория по названию модели.
121
163
 
122
164
  ```js
123
- schema.defineModel({
124
- name: 'latLng',
125
- properties: {
126
- lat: DataType.NUMBER, // краткое определение поля "lat"
127
- lng: { // расширенное определение поля "lng"
128
- type: DataType.NUMBER, // тип допустимого значения
129
- required: true, // исключает null и undefined
130
- },
131
- },
132
- });
165
+ const productRep = schema.getRepository('product');
133
166
  ```
134
167
 
135
- **Типы данных:**
168
+ ## Источник данных
169
+
170
+ Источник хранит название выбранного адаптера и его настройки. Определить
171
+ новый источник можно методом `defineDatasource` экземпляра схемы.
136
172
 
137
- - `DataType.ANY`
138
- - `DataType.STRING`
139
- - `DataType.NUMBER`
140
- - `DataType.BOOLEAN`
141
- - `DataType.ARRAY`
142
- - `DataType.OBJECT`
173
+ **Параметры**
143
174
 
144
- Модель `latLng` всего лишь описывает структуру объекта координат, тогда
145
- как торговая точка должна иметь реальную таблицу в базе. По аналогии с
146
- предыдущим примером, добавим модель `place`, но дополнительно укажем
147
- источник данных в параметре `datasource`
175
+ - `name: string` уникальное название
176
+ - `adapter: string` выбранный адаптер
177
+ - параметры адаптера (если имеются)
178
+
179
+ **Примеры**
180
+
181
+ Определение нового источника.
148
182
 
149
183
  ```js
150
- schema.defineModel({
151
- name: 'place',
152
- datasource: 'myMemory', // выбранный источник данных
153
- properties: {
154
- name: DataType.STRING, // поле для названия торговой точки
155
- location: { // поле объекта координат
156
- type: DataType.OBJECT, // допускать только объекты
157
- model: 'latLng', // определение структуры объекта
158
- },
159
- },
184
+ schema.defineDatasource({
185
+ name: 'myMemory', // название нового источника
186
+ adapter: 'memory', // выбранный адаптер
160
187
  });
161
188
  ```
162
189
 
163
- В примере выше мы использовали модель `latLng` как структуру допустимого
164
- значения поля `location`. Возможный документ данной коллекции может
165
- выглядеть так:
190
+ Передача дополнительных параметров адаптера.
166
191
 
167
- ```json
168
- {
169
- "id": 1,
170
- "name": "Burger King at Avenue Mall",
171
- "location": {
172
- "lat": 32.412891,
173
- "lng": 34.7660061
174
- }
175
- }
192
+ ```js
193
+ schema.defineDatasource({
194
+ name: 'myMongodb',
195
+ adapter: 'mongodb',
196
+ // параметры адаптера "mongodb"
197
+ host: '127.0.0.1',
198
+ port: 27017,
199
+ database: 'myDatabase',
200
+ });
176
201
  ```
177
202
 
178
- Стоит обратить внимание, что мы могли бы не объявлять параметр `properties`,
179
- при этом теряя возможность проверки данных перед записью в базу.
203
+ ## Модель
204
+
205
+ Описывает структуру документа коллекции и связи к другим моделям. Определить
206
+ новую модель можно методом `defineModel` экземпляра схемы.
207
+
208
+ **Параметры**
209
+
210
+ - `name: string` название модели (обязательно)
211
+ - `base: string` название наследуемой модели
212
+ - `tableName: string` название коллекции в базе
213
+ - `datasource: string` выбранный источник данных
214
+ - `properties: object` определения свойств (см. [Свойства](#Свойства))
215
+ - `relations: object` определения связей (см. [Связи](#Связи))
216
+
217
+ **Примеры**
218
+
219
+ Определение модели со свойствами указанного типа.
180
220
 
181
221
  ```js
182
222
  schema.defineModel({
183
- name: 'place',
184
- adapter: 'myMemory',
185
- // параметр "properties" не указан
223
+ name: 'user', // название новой модели
224
+ properties: { // свойства модели
225
+ name: DataType.STRING,
226
+ age: DataType.NUMBER,
227
+ },
186
228
  });
187
229
  ```
188
230
 
189
- **Параметры поля:**
231
+ ## Свойства
232
+
233
+ Параметр `properties` находится в составе модели и принимает объект, ключи
234
+ которого являются свойствами этой модели, а значением тип свойства или объект
235
+ с дополнительными параметрами.
236
+
237
+ **Тип данных**
238
+
239
+ - `DataType.ANY` разрешено любое значение
240
+ - `DataType.STRING` только значение типа `string`
241
+ - `DataType.NUMBER` только значение типа `number`
242
+ - `DataType.BOOLEAN` только значение типа `boolean`
243
+ - `DataType.ARRAY` только значение типа `array`
244
+ - `DataType.OBJECT` только значение типа `object`
245
+
246
+ **Параметры**
190
247
 
191
248
  - `type: string` тип допустимого значения (обязательно)
192
249
  - `itemType: string` тип элемента массива (для `type: 'array'`)
193
250
  - `model: string` модель объекта (для `type: 'object'`)
194
- - `primaryKey: boolean` объявить поле первичным ключом
251
+ - `primaryKey: boolean` объявить свойство первичным ключом
195
252
  - `columnName: string` переопределение названия колонки
196
253
  - `columnType: string` тип колонки (определяется адаптером)
197
- - `required: boolean` объявить поле обязательным
254
+ - `required: boolean` объявить свойство обязательным
198
255
  - `default: any` значение по умолчанию
199
256
 
200
- ### Репозиторий
257
+ **Примеры**
201
258
 
202
- В отличие от `latLng`, модель `place` имеет источник данных с названием
203
- `myMemory`, который был объявлен ранее. Наличие источника позволяет получить
204
- репозиторий по названию модели.
259
+ Краткое определение свойств модели.
205
260
 
206
261
  ```js
207
- const rep = schema.getRepository('place');
262
+ schema.defineModel({
263
+ name: 'city',
264
+ properties: { // свойства модели
265
+ name: DataType.STRING, // тип свойства "string"
266
+ population: DataType.NUMBER, // тип свойства "number"
267
+ },
268
+ });
208
269
  ```
209
270
 
210
- **Методы:**
211
-
212
- - `create(data, filter = undefined)`
213
- - `replaceById(id, data, filter = undefined)`
214
- - `replaceOrCreate(data, filter = undefined)`
215
- - `patch(data, where = undefined)`
216
- - `patchById(id, data, filter = undefined)`
217
- - `find(filter = undefined)`
218
- - `findOne(filter = undefined)`
219
- - `findById(id, filter = undefined)`
220
- - `delete(where = undefined)`
221
- - `deleteById(id)`
222
- - `exists(id)`
223
- - `count(where = undefined)`
224
-
225
- #### create(data, filter = undefined)
226
-
227
- Создадим торговую точку методом `create` используя репозиторий из примера
228
- выше. Метод возвращает документ, который был записан в базу, включая присвоенный
229
- идентификатор.
271
+ Расширенное определение свойств модели.
230
272
 
231
273
  ```js
232
- const place = await rep.create({
233
- "name": "Burger King at Avenue Mall",
234
- "location": {
235
- "lat": 32.412891,
236
- "lng": 34.7660061
274
+ schema.defineModel({
275
+ name: 'city',
276
+ properties: { // свойства модели
277
+ name: {
278
+ type: DataType.STRING, // тип свойства "string" (обязательно)
279
+ required: true, // исключение значений undefined и null
280
+ },
281
+ population: {
282
+ type: DataType.NUMBER, // тип свойства "number" (обязательно)
283
+ default: 0, // значение по умолчанию
284
+ },
237
285
  },
238
286
  });
239
-
240
- console.log(place);
241
- // {
242
- // "id": 1,
243
- // "name": "Burger King at Avenue Mall",
244
- // "location": {
245
- // "lat": 32.412891,
246
- // "lng": 34.7660061
247
- // }
248
- // }
249
287
  ```
250
288
 
251
- #### replaceById(id, data, filter = undefined)
252
-
253
- Добавленный в базу документ можно полностью заменить зная его идентификатор.
254
- Воспользуемся методом `replaceById`, который перезапишет данные по значению
255
- первичного ключа.
289
+ Фабричное значение по умолчанию. Возвращаемое значение функции будет
290
+ определено в момент записи документа.
256
291
 
257
292
  ```js
258
- // {
259
- // "id": 1,
260
- // "name": "Burger King at Avenue Mall",
261
- // "location": {
262
- // "lat": 32.412891,
263
- // "lng": 34.7660061
264
- // }
265
- // }
266
- const result = rep.replaceById(place.id, {
267
- name: 'Terminal 21 Shopping Mall',
268
- address: 'Sukhumvit 19 Alley'
293
+ schema.defineModel({
294
+ name: 'article',
295
+ properties: { // свойства модели
296
+ tags: {
297
+ type: DataType.ARRAY, // тип свойства "array" (обязательно)
298
+ itemType: DataType.STRING, // тип элемента "string"
299
+ default: () => [], // фабричное значение
300
+ },
301
+ createdAt: {
302
+ type: DataType.STRING, // тип свойства "string" (обязательно)
303
+ default: () => new Date().toISOString(), // фабричное значение
304
+ },
305
+ },
269
306
  });
270
-
271
- console.log(result);
272
- // {
273
- // "id": 1,
274
- // "name": "Terminal 21 Shopping Mall",
275
- // "address": "Sukhumvit 19 Alley"
276
- // }
277
307
  ```
278
308
 
279
- #### patchById(id, data, filter = undefined)
309
+ ## Репозиторий
280
310
 
281
- В отличие от `replaceById`, данный метод не удаляет поля, которые
282
- не были переданы, что позволяет обновить только часть документа,
283
- не затрагивая другие данные.
311
+ Выполняет операции чтения и записи документов определенной модели.
312
+ Получить репозиторий можно методом `getRepository` экземпляра схемы.
284
313
 
285
- ```js
286
- // {
287
- // "id": 1,
288
- // "name": "Terminal 21 Shopping Mall",
289
- // "address": "Sukhumvit 19 Alley"
290
- // }
291
- const result = rep.patchById(place.id, {
292
- address: 'Moo 6',
293
- city: 'Pattaya',
294
- });
314
+ **Методы**
295
315
 
296
- console.log(result);
297
- // {
298
- // "id": 1,
299
- // "name": "Terminal 21 Shopping Mall",
300
- // "address": "Moo 6",
301
- // "city": "Pattaya"
302
- // }
303
- ```
316
+ - `create(data, filter = undefined)` добавить новый документ
317
+ - `replaceById(id, data, filter = undefined)` заменить весь документ
318
+ - `replaceOrCreate(data, filter = undefined)` заменить или создать новый
319
+ - `patchById(id, data, filter = undefined)` частично обновить документ
320
+ - `patch(data, where = undefined)` обновить все документы или по условию
321
+ - `find(filter = undefined)` найти все документы или по условию
322
+ - `findOne(filter = undefined)` найти первый документ или по условию
323
+ - `findById(id, filter = undefined)` найти документ по идентификатору
324
+ - `delete(where = undefined)` удалить все документы или по условию
325
+ - `deleteById(id)` удалить документ по идентификатору
326
+ - `exists(id)` проверить существование по идентификатору
327
+ - `count(where = undefined)` подсчет всех документов или по условию
304
328
 
305
- ## Пример
329
+ **Аргументы**
306
330
 
307
- Создаем модель `user`
331
+ - `id: number|string` идентификатор (первичный ключ)
332
+ - `data: object` объект отражающий состав документа
333
+ - `where: object` параметры выборки (см. [Фильтрация](#Фильтрация))
334
+ - `filter: object` параметры возвращаемого результата (см. [Фильтрация](#Фильтрация))
308
335
 
309
- ```js
310
- schema.defineModel({
311
- name: 'user', // название модели
312
- adapter: 'myMemory', // выбранный источник
313
- properties: { // поля модели
314
- name: 'string',
315
- age: 'number',
316
- },
317
- });
318
- ```
336
+ **Примеры**
319
337
 
320
- Получаем репозиторий модели `user`
338
+ Получение репозитория по названию модели.
321
339
 
322
340
  ```js
323
- const userRep = schema.getRepository('user');
341
+ const countryRep = schema.getRepository('country');
324
342
  ```
325
343
 
326
- Добавляем новую запись методом `create`
344
+ Добавление нового документа в коллекцию.
327
345
 
328
346
  ```js
329
- const fedor = await userRep.create({
330
- name: 'Fedor',
331
- age: 24,
347
+ const res = await countryRep.create({
348
+ name: 'Russia',
349
+ population: 143400000,
332
350
  });
333
351
 
334
- console.log(fedor);
352
+ console.log(res);
335
353
  // {
336
- // id: 1,
337
- // name: 'Fedor',
338
- // age: 24,
354
+ // "id": 1,
355
+ // "name": "Russia",
356
+ // "population": 143400000,
339
357
  // }
340
358
  ```
341
359
 
342
- Изменяем данные методом `patchById`
360
+ Поиск документа по идентификатору.
343
361
 
344
362
  ```js
345
- const result = await userRep.patchById(
346
- fedor.id,
347
- {age: 30},
348
- );
363
+ const res = await countryRep.findById(1);
349
364
 
350
- console.log(result);
365
+ console.log(res);
351
366
  // {
352
- // id: 1,
353
- // name: 'Fedor',
354
- // age: 30,
367
+ // "id": 1,
368
+ // "name": "Russia",
369
+ // "population": 143400000,
355
370
  // }
356
371
  ```
357
372
 
358
- Удаляем по идентификатору методом `deleteById`
373
+ Удаление документа по идентификатору.
359
374
 
360
375
  ```js
361
- await userRep.deleteById(fedor.id); // true
376
+ const res = await countryRep.deleteById(1);
377
+
378
+ console.log(res); // true
362
379
  ```
363
380
 
364
- ## Репозиторий
381
+ ## Фильтрация
365
382
 
366
- Выполняет операции чтения и записи определенной коллекции.
383
+ Некоторые методы репозитория принимают объект настроек влияющий
384
+ на возвращаемый результат. Максимально широкий набор таких настроек
385
+ имеет первый параметр метода `find`, где ожидается объект содержащий
386
+ набор опций указанных ниже.
367
387
 
368
- Методы:
388
+ - `where: object` объект выборки
389
+ - `order: string[]` указание порядка
390
+ - `limit: number` ограничение количества документов
391
+ - `skip: number` пропуск документов
392
+ - `fields: string[]` выбор необходимых свойств модели
393
+ - `include: object` включение связанных данных в результат
369
394
 
370
- - `create(data, filter = undefined)`
371
- - `replaceById(id, data, filter = undefined)`
372
- - `replaceOrCreate(data, filter = undefined)`
373
- - `patch(data, where = undefined)`
374
- - `patchById(id, data, filter = undefined)`
375
- - `find(filter = undefined)`
376
- - `findOne(filter = undefined)`
377
- - `findById(id, filter = undefined)`
378
- - `delete(where = undefined)`
379
- - `deleteById(id)`
380
- - `exists(id)`
381
- - `count(where = undefined)`
395
+ ### where
382
396
 
383
- Получение репозитория модели:
397
+ Параметр принимает объект с условиями выборки и поддерживает широкий
398
+ набор операторов сравнения.
384
399
 
385
- ```js
386
- import {Schema} from '@e22m4u/js-repository';
400
+ `{foo: 'bar'}` поиск по значению свойства `foo`
401
+ `{foo: {eq: 'bar'}}` оператор равенства `eq`
402
+ `{foo: {neq: 'bar'}}` оператор неравенства `neq`
403
+ `{foo: {gt: 5}}` оператор "больше" `gt`
404
+ `{foo: {lt: 10}}` оператор "меньше" `lt`
405
+ `{foo: {gte: 5}}` оператор "больше или равно" `gte`
406
+ `{foo: {lte: 10}}` оператор "меньше или равно" `lte`
407
+ `{foo: {inq: ['bar', 'baz']}}` равенство одного из значений `inq`
408
+ `{foo: {nin: ['bar', 'baz']}}` исключение значений массива `nin`
409
+ `{foo: {between: [5, 10]}}` оператор диапазона `between`
410
+ `{foo: {exists: true}}` оператор наличия значения `exists`
411
+ `{foo: {like: 'bar'}}` оператор поиска подстроки `like`
412
+ `{foo: {ilike: 'BaR'}}` регистронезависимая версия `ilike`
413
+ `{foo: {nlike: 'bar'}}` оператор исключения подстроки `nlike`
414
+ `{foo: {nilike: 'BaR'}}` регистронезависимая версия `nilike`
415
+ `{foo: {regexp: 'ba.+'}}` оператор регулярного выражения `regexp`
416
+ `{foo: {regexp: 'ba.+', flags: 'i'}}` флаги регулярного выражения
387
417
 
388
- const schema = new Schema();
389
- // создаем источник
390
- schema.defineDatasource({name: 'myDatasource', adapter: 'memory'});
391
- // создаем модель
392
- schema.defineModel({name: 'myModel', datasource: 'myDatasource'});
393
- // получаем репозиторий по названию модели
394
- const repositorty = schema.getRepository('myModel');
395
- ```
418
+ *i. Условия можно объединять операторами `and`, `or` и `nor`.*
396
419
 
397
- Переопределение конструктора:
420
+ **Примеры**
398
421
 
399
- ```js
400
- import {Schema} from '@e22m4u/js-repository';
401
- import {Repository} from '@e22m4u/js-repository';
402
- import {RepositoryRegistry} from '@e22m4u/js-repository';
422
+ Применение условий выборки при подсчете документов.
403
423
 
404
- class MyRepository extends Repository {
405
- /*...*/
406
- }
424
+ ```js
425
+ const res = await rep.count({
426
+ authorId: 251,
427
+ publishedAt: {
428
+ lte: '2023-12-02T14:00:00.000Z',
429
+ },
430
+ });
431
+ ```
407
432
 
408
- const schema = new Schema();
409
- schema.get(RepositoryRegistry).setRepositoryCtor(MyRepository);
410
- // теперь schema.getRepository(modelName) будет возвращать
411
- // экземпляр класса MyRepository
412
- ```
413
-
414
- ### Filter
415
-
416
- Большинство методов репозитория принимают объект `filter` для
417
- фильтрации возвращаемого ответа. Описание параметров объекта:
418
-
419
- - **where** *(условия выборки данных из базы)*
420
- примеры:
421
- `where: {foo: 'bar'}` поиск по значению поля `foo`
422
- `where: {foo: {eq: 'bar'}}` оператор равенства `eq`
423
- `where: {foo: {neq: 'bar'}}` оператор неравенства `neq`
424
- `where: {foo: {gt: 5}}` оператор "больше" `gt`
425
- `where: {foo: {lt: 10}}` оператор "меньше" `lt`
426
- `where: {foo: {gte: 5}}` оператор "больше или равно" `gte`
427
- `where: {foo: {lte: 10}}` оператор "меньше или равно" `lte`
428
- `where: {foo: {inq: ['bar', 'baz']}}` равенство одного из значений `inq`
429
- `where: {foo: {nin: ['bar', 'baz']}}` исключение значений массива `nin`
430
- `where: {foo: {between: [5, 10]}}` оператор диапазона `between`
431
- `where: {foo: {exists: true}}` оператор наличия значения `exists`
432
- `where: {foo: {like: 'bar'}}` оператор поиска подстроки `like`
433
- `where: {foo: {ilike: 'BaR'}}` регистронезависимая версия `ilike`
434
- `where: {foo: {nlike: 'bar'}}` оператор исключения подстроки `nlike`
435
- `where: {foo: {nilike: 'BaR'}}` регистронезависимая версия `nilike`
436
- `where: {foo: {regexp: 'ba.+'}}` оператор регулярного выражения `regexp`
437
- `where: {foo: {regexp: 'ba.+', flags: 'i'}}` флаги регулярного выражения
438
-
439
-
440
- - **order** *(упорядочить записи по полю)*
441
- примеры:
442
- `order: 'foo'` порядок по полю `foo`
443
- `order: 'foo ASC'` явное указание порядка
444
- `order: 'foo DESC'` инвертировать порядок
445
- `order: ['foo', 'bar ASC', 'baz DESC']` по нескольким полям
446
-
447
-
448
- - **limit** *(не более N записей)*
449
- примеры:
450
- `limit: 0` не ограничивать
451
- `limit: 14` не более 14-и
452
-
453
-
454
- - **skip** *(пропуск первых N записей)*
455
- примеры:
456
- `skip: 0` выборка без пропуска
457
- `skip: 10` пропустить 10 объектов выборки
458
-
459
-
460
- - **include** *(включение связанных данных в результат)*
461
- примеры:
462
- `include: 'foo'` включение связи `foo`
463
- `include: ['foo', 'bar']` включение `foo` и `bar`
464
- `include: {foo: 'bar'}` включение вложенной связи `foo`
433
+ Применение оператора `or` при удалении документов.
465
434
 
466
- ## Источник данных
435
+ ```js
436
+ const res = await rep.delete({
437
+ or: [
438
+ {draft: true},
439
+ {title: {like: 'draft'}},
440
+ ],
441
+ });
442
+ ```
467
443
 
468
- Определяет настройки и способ подключения к базе.
444
+ ### order
469
445
 
470
- Параметры:
446
+ Параметр упорядочивает выборку по указанным свойствам модели. Обратное
447
+ направление порядка можно задать постфиксом `DESC` в названии свойства.
471
448
 
472
- - `name: string` название нового источника
473
- - `adapter: string` выбранный адаптер базы данных
449
+ **Примеры**
474
450
 
475
- Пример:
451
+ Упорядочить по полю `createdAt`
476
452
 
477
453
  ```js
478
- schema.defineDatasource({
479
- name: 'myDatasource',
480
- adapter: 'memory',
454
+ const res = await rep.find({
455
+ order: 'createdAt',
481
456
  });
482
457
  ```
483
458
 
484
- Адаптер может иметь параметры, которые передаются
485
- при определении источника.
459
+ Упорядочить по полю `createdAt` в обратном порядке.
486
460
 
487
- Пример:
461
+ ```js
462
+ const res = await rep.find({
463
+ order: 'createdAt DESC',
464
+ });
465
+ ```
466
+
467
+ Упорядочить по нескольким свойствам в разных направлениях.
488
468
 
489
469
  ```js
490
- schema.defineDatasource({
491
- name: 'myDatasource',
492
- adapter: 'mongodb',
493
- // параметры адаптера:
494
- host: '127.0.0.1',
495
- port: 27017,
470
+ const res = await rep.find({
471
+ order: [
472
+ 'title',
473
+ 'price ASC',
474
+ 'featured DESC',
475
+ ],
496
476
  });
497
477
  ```
498
478
 
499
- ## Модель
479
+ *i. Направление порядка `ASC` указывать необязательно.*
500
480
 
501
- Описывает набор полей и связей к другим моделям.
481
+ ### include
502
482
 
503
- Параметры:
483
+ Параметр включает связанные документы в результат вызываемого метода.
484
+ Названия включаемых связей должны быть определены в текущей модели.
485
+ (см. [Связи](#Связи))
504
486
 
505
- - `name: string` название новой модели
506
- - `datasource: string` выбранный источник данных
507
- - `properties: object` определения полей модели
508
- - `relations: object` определения связей модели
487
+ **Примеры**
509
488
 
510
- Пример:
489
+ Включение связи по названию.
511
490
 
512
491
  ```js
513
- schema.defineModel({
514
- name: 'myModel',
515
- datasource: 'myDatasource',
516
- properties: {...}, // см. ниже
517
- relations: {...}, // см. ниже
492
+ const res = await rep.find({
493
+ include: 'city',
518
494
  });
519
495
  ```
520
496
 
521
- ## Поля
522
-
523
- Параметр `properties` описывает набор полей и их настройки.
524
-
525
- Типы:
526
-
527
- - `string`
528
- - `number`
529
- - `boolean`
530
- - `array`
531
- - `object`
532
- - `any`
533
-
534
- Пример:
497
+ Включение вложенных связей.
535
498
 
536
499
  ```js
537
- schema.defineModel({
538
- // ...
539
- properties: {
540
- prop1: 'string',
541
- prop2: 'number',
542
- prop3: 'boolean',
543
- prop4: 'array',
544
- prop5: 'object',
545
- prop6: 'any',
500
+ const res = await rep.find({
501
+ include: {
502
+ city: 'country',
546
503
  },
547
504
  });
548
505
  ```
549
506
 
550
- Расширенные параметры:
507
+ Включение нескольких связей массивом.
551
508
 
552
- - `type: string` тип хранимого значения
553
- - `itemType: string` тип элемента массива (для `type: 'array'`)
554
- - `model: string` модель объекта (для `type: 'object'`)
555
- - `primaryKey: boolean` объявить поле первичным ключом
556
- - `columnName: string` переопределение названия колонки
557
- - `columnType: string` тип колонки (определяется адаптером)
558
- - `required: boolean` объявить поле обязательным
559
- - `default: any` значение по умолчанию для `undefined`
509
+ ```js
510
+ const res = await rep.find({
511
+ include: [
512
+ 'city',
513
+ 'address',
514
+ 'employees'
515
+ ],
516
+ });
517
+ ```
560
518
 
561
- Пример:
519
+ Использование фильтрации включаемых документов.
562
520
 
563
521
  ```js
564
- schema.defineModel({
565
- // ...
566
- properties: {
567
- prop1: {
568
- type: 'string',
569
- primaryKey: true,
570
- },
571
- prop2: {
572
- type: 'boolean',
573
- required: true,
574
- },
575
- prop3: {
576
- type: 'number',
577
- default: 100,
578
- },
579
- prop3: {
580
- type: 'string',
581
- // фабричное значение
582
- default: () => new Date().toISOString(),
583
- },
584
- prop4: {
585
- type: 'array',
586
- itemType: 'string',
587
- },
588
- prop5: {
589
- type: 'object',
590
- model: 'objectModel',
522
+ const res = await rep.find({
523
+ include: {
524
+ relation: 'employees', // название связи
525
+ scope: { // фильтрация документов "employees"
526
+ where: {hidden: false}, // условия выборки
527
+ order: 'id', // порядок документов
528
+ limit: 10, // ограничение количества
529
+ skip: 5, // пропуск документов
530
+ fields: ['name', 'surname'], // только указанные поля
531
+ include: 'city', // включение связей для "employees"
591
532
  },
592
533
  },
593
534
  });
@@ -595,188 +536,237 @@ schema.defineModel({
595
536
 
596
537
  ## Связи
597
538
 
598
- Параметр `relations` описывает набор связей к другим моделям.
599
-
600
- Понятия:
539
+ Параметр `relations` находится в составе определения модели и принимает
540
+ объект, ключ которого является названием связи, а значением объект
541
+ с параметрами.
601
542
 
602
- - источник связи
603
- *- модель в которой определена данная связь*
604
- - целевая модель
605
- *- модель на которую ссылается источник связи*
606
-
607
- Типы:
608
-
609
- - `belongsTo` - ссылка на целевую модель находится в источнике
610
- - `hasOne` - ссылка на источник находится в целевой модели (one-to-one)
611
- - `hasMany` - ссылка на источник находится в целевой модели (one-to-many)
612
- - `referencesMany` - массив ссылок на целевую модель находится в источнике
613
-
614
- Параметры:
543
+ **Параметры**
615
544
 
616
545
  - `type: string` тип связи
617
- - `model: string` целевая модель
618
- - `foreignKey: string` поле для идентификатора цели
546
+ - `model: string` название целевой модели
547
+ - `foreignKey: string` свойство текущей модели для идентификатора цели
619
548
  - `polymorphic: boolean|string` объявить связь полиморфной*
620
- - `discriminator: string` поле для названия целевой модели (для `polymorphic: true`)
549
+ - `discriminator: string` свойство текущей модели для названия целевой*
550
+
551
+ *i. Полиморфный режим позволяет динамически определять целевую модель
552
+ по ее названию, которое хранит документ в свойстве-дискриминаторе.*
621
553
 
622
- *i. Полиморфный режим `belongsTo` позволяет динамически определять цель связи,
623
- где имя целевой модели хранится в отдельном поле, рядом с `foreignKey`*
554
+ **Тип связи**
624
555
 
625
- #### BelongsTo
556
+ - `belongsTo` - текущая модель содержит свойство для идентификатора цели
557
+ - `hasOne` - обратная сторона `belongsTo` по принципу "один к одному"
558
+ - `hasMany` - обратная сторона `belongsTo` по принципу "один ко многим"
559
+ - `referencesMany` - документ содержит массив с идентификаторами целевой модели
626
560
 
627
- Связь заказа к покупателю через поле `customerId`
561
+ **Примеры**
562
+
563
+ Объявление связи `belongsTo`
628
564
 
629
565
  ```js
630
566
  schema.defineModel({
631
- // ...
567
+ name: 'user',
632
568
  relations: {
633
- // ...
634
- customer: {
635
- type: 'belongsTo',
636
- model: 'customer',
637
- foreignKey: 'customerId', // опционально
569
+ role: { // название связи
570
+ type: RelationType.BELONGS_TO, // текущая модель ссылается на целевую
571
+ model: 'role', // название целевой модели
572
+ foreignKey: 'roleId', // внешний ключ (необязательно)
573
+ // если "foreignKey" не указан, то свойство внешнего
574
+ // ключа формируется согласно названию связи
575
+ // с добавлением постфикса "Id"
638
576
  },
639
577
  },
640
578
  });
641
579
  ```
642
580
 
643
- Полиморфная версия
581
+ Объявление связи `hasMany`
644
582
 
645
583
  ```js
646
584
  schema.defineModel({
647
- // ...
585
+ name: 'role',
648
586
  relations: {
649
- // ...
650
- customer: {
651
- type: 'belongsTo',
652
- polymorphic: true,
653
- foreignKey: 'customerId', // опционально
654
- discriminator: 'customerType', // опционально
587
+ users: { // название связи
588
+ type: RelationType.HAS_MANY, // целевая модель ссылается на текущую
589
+ model: 'user', // название целевой модели
590
+ foreignKey: 'roleId', // внешний ключ из целевой модели на текущую
655
591
  },
656
592
  },
657
593
  });
658
594
  ```
659
595
 
660
- #### HasOne (one-to-one)
661
-
662
- Связь покупателя к заказу, как обратная сторона `belongsTo`
596
+ Объявление связи `referencesMany`
663
597
 
664
598
  ```js
665
599
  schema.defineModel({
666
- // ...
600
+ name: 'article',
667
601
  relations: {
668
- // ...
669
- order: {
670
- type: 'hasOne',
671
- model: 'order',
672
- foreignKey: 'customerId', // опционально
602
+ categories: { // название связи
603
+ type: RelationType.REFERENCES_MANY, // связь через массив идентификаторов
604
+ model: 'category', // название целевой модели
605
+ foreignKey: 'categoryIds', // внешний ключ (необязательно)
606
+ // если "foreignKey" не указан, то свойство внешнего
607
+ // ключа формируется согласно названию связи
608
+ // с добавлением постфикса "Ids"
673
609
  },
674
610
  },
675
611
  });
676
612
  ```
677
613
 
678
- Обратная сторона полиморфной версии `belongsTo`
614
+ Полиморфная версия `belongsTo`
679
615
 
680
616
  ```js
681
617
  schema.defineModel({
682
- // ...
618
+ name: 'file',
683
619
  relations: {
684
- // ...
685
- order: {
686
- type: 'hasOne',
687
- model: 'order',
688
- polymorphic: 'customer', // имя связи целевой модели
620
+ reference: { // название связи
621
+ type: RelationType.BELONGS_TO, // текущая модель ссылается на целевую
622
+ // полиморфный режим позволяет хранить название целевой модели
623
+ // в свойстве-дискриминаторе, которое формируется согласно
624
+ // названию связи с постфиксом "Type", и в данном случае
625
+ // название целевой модели хранит "referenceType",
626
+ // а идентификатор документа "referenceId"
627
+ polymorphic: true,
689
628
  },
690
629
  },
691
630
  });
692
631
  ```
693
632
 
694
- Явное указание `foreignKey` и `discriminator`
633
+ Полиморфная версия `belongsTo` с указанием свойств.
695
634
 
696
635
  ```js
697
636
  schema.defineModel({
698
- // ...
637
+ name: 'file',
699
638
  relations: {
700
- // ...
701
- order: {
702
- type: 'hasOne',
703
- model: 'order',
704
- polymorphic: true, // true вместо имени модели
705
- foreignKey: 'customerId', // поле целевой модели
706
- discriminator: 'customerType', // поле целевой модели
639
+ reference: { // название связи
640
+ type: RelationType.BELONGS_TO, // текущая модель ссылается на целевую
641
+ polymorphic: true, // название целевой модели хранит дискриминатор
642
+ foreignKey: 'referenceId', // свойство для идентификатора цели
643
+ discriminator: 'referenceType', // свойство для названия целевой модели
707
644
  },
708
645
  },
709
- });
646
+ })
710
647
  ```
711
648
 
712
- #### HasMany (one-to-many)
713
-
714
- Связь покупателя к заказам, как обратная сторона `belongsTo`
649
+ Полиморфная версия `hasMany` с указанием названия связи целевой модели.
715
650
 
716
651
  ```js
717
652
  schema.defineModel({
718
- // ...
653
+ name: 'letter',
719
654
  relations: {
720
- // ...
721
- orders: {
722
- type: 'hasMany',
723
- model: 'order',
724
- foreignKey: 'customerId', // опционально
655
+ attachments: { // название связи
656
+ type: RelationType.HAS_MANY, // целевая модель ссылается на текущую
657
+ model: 'file', // название целевой модели
658
+ polymorphic: 'reference', // название полиморфной связи целевой модели
725
659
  },
726
660
  },
727
- });
661
+ })
728
662
  ```
729
663
 
730
- Обратная сторона полиморфной версии `belongsTo`
664
+ Полиморфная версия `hasMany` с указанием свойств целевой модели.
731
665
 
732
666
  ```js
733
667
  schema.defineModel({
734
- // ...
668
+ name: 'letter',
735
669
  relations: {
736
- // ...
737
- orders: {
738
- type: 'hasMany',
739
- model: 'order',
740
- polymorphic: 'customer', // имя связи целевой модели
670
+ attachments: { // название связи
671
+ type: RelationType.HAS_MANY, // целевая модель ссылается на текущую
672
+ model: 'file', // название целевой модели
673
+ polymorphic: true, // название текущей модели находится в дискриминаторе
674
+ foreignKey: 'referenceId', // свойство целевой модели для идентификатора
675
+ discriminator: 'referenceType', // свойство целевой модели для названия текущей
741
676
  },
742
677
  },
743
- });
678
+ })
744
679
  ```
745
680
 
746
- Явное указание `foreignKey` и `discriminator`
681
+ ## Расширение
682
+
683
+ Метод `getRepository` экземпляра схемы проверяет наличие существующего
684
+ репозитория для указанной модели и возвращает его. В противном случае
685
+ создается новый экземпляр, который будет сохранен для последующих
686
+ обращений к методу.
747
687
 
748
688
  ```js
749
- schema.defineModel({
750
- // ...
751
- relations: {
752
- // ...
753
- orders: {
754
- type: 'hasMany',
755
- model: 'order',
756
- polymorphic: true, // true вместо имени модели
757
- foreignKey: 'customerId', // поле целевой модели
758
- discriminator: 'customerType', // поле целевой модели
759
- },
760
- },
761
- });
762
- ```
689
+ import {Schema} from '@e22m4u/js-repository';
690
+ import {Repository} from '@e22m4u/js-repository';
763
691
 
764
- #### ReferencesMany
692
+ // const schema = new Schema();
693
+ // schema.defineDatasource ...
694
+ // schema.defineModel ...
765
695
 
766
- Связь покупателя к заказам через поле `orderIds`
696
+ const rep1 = schema.getRepository('model');
697
+ const rep2 = schema.getRepository('model');
698
+ console.log(rep1 === rep2); // true
699
+ ```
700
+
701
+ Подмена стандартного конструктора репозитория выполняется методом
702
+ `setRepositoryCtor` сервиса `RepositoryRegistry`, который находится
703
+ в контейнере экземпляра схемы. После чего все новые репозитории будут
704
+ создаваться указанным конструктором вместо стандартного.
767
705
 
768
706
  ```js
707
+ import {Schema} from '@e22m4u/js-repository';
708
+ import {Repository} from '@e22m4u/js-repository';
709
+ import {RepositoryRegistry} from '@e22m4u/js-repository';
710
+
711
+ class MyRepository extends Repository {
712
+ /*...*/
713
+ }
714
+
715
+ // const schema = new Schema();
716
+ // schema.defineDatasource ...
717
+ // schema.defineModel ...
718
+
719
+ schema.get(RepositoryRegistry).setRepositoryCtor(MyRepository);
720
+ const rep = schema.getRepository('model');
721
+ console.log(rep instanceof MyRepository); // true
722
+ ```
723
+
724
+ *i. Так как экземпляры репозитория кэшируется, то замену конструктора
725
+ следует выполнять до обращения к методу `getRepository`.*
726
+
727
+ ## TypeScript
728
+
729
+ Получение типизированного репозитория с указанием интерфейса модели.
730
+
731
+ ```ts
732
+ import {Schema} from '@e22m4u/js-repository';
733
+ import {DataType} from '@e22m4u/js-repository';
734
+ import {RelationType} from '@e22m4u/js-repository';
735
+
736
+ // const schema = new Schema();
737
+ // schema.defineDatasource ...
738
+ // schema.defineModel ...
739
+
740
+ // определение модели "city"
769
741
  schema.defineModel({
770
- // ...
742
+ name: 'city',
743
+ datasource: 'myDatasource',
744
+ properties: {
745
+ title: DataType.STRING,
746
+ timeZone: DataType.STRING,
747
+ },
771
748
  relations: {
772
- // ...
773
- orders: {
774
- type: 'referencesMany',
775
- model: 'order',
776
- foreignKey: 'orderIds', // опционально
749
+ country: {
750
+ type: RelationType.BELONGS_TO,
751
+ model: 'country',
777
752
  },
778
753
  },
779
754
  });
755
+
756
+ // определение интерфейса "city"
757
+ interface City {
758
+ id: number;
759
+ title?: string;
760
+ timeZone?: string;
761
+ countryId?: number;
762
+ country?: Country;
763
+ // открыть свойства (опционально)
764
+ [property: string]: unknown;
765
+ }
766
+
767
+ // получаем репозиторий по названию модели
768
+ // указывая ее тип и тип идентификатора
769
+ const cityRep = schema.getRepository<City, number>('city');
780
770
  ```
781
771
 
782
772
  ## Тесты