@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.
- package/.eslintignore +1 -0
- package/.husky/pre-commit +1 -0
- package/README.md +516 -526
- package/docs/.nojekyll +1 -0
- package/docs/assets/highlight.css +99 -0
- package/docs/assets/main.js +59 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1394 -0
- package/docs/classes/Adapter.html +38 -0
- package/docs/classes/AdapterLoader.html +16 -0
- package/docs/classes/AdapterRegistry.html +16 -0
- package/docs/classes/BelongsToResolver.html +18 -0
- package/docs/classes/DatasourceDefinitionValidator.html +16 -0
- package/docs/classes/DefinitionRegistry.html +26 -0
- package/docs/classes/FieldsClauseTool.html +20 -0
- package/docs/classes/HasManyResolver.html +20 -0
- package/docs/classes/HasOneResolver.html +20 -0
- package/docs/classes/IncludeClauseTool.html +24 -0
- package/docs/classes/InvalidArgumentError.html +14 -0
- package/docs/classes/InvalidOperatorValueError.html +14 -0
- package/docs/classes/ModelDataSanitizer.html +16 -0
- package/docs/classes/ModelDataValidator.html +18 -0
- package/docs/classes/ModelDefinitionUtils.html +46 -0
- package/docs/classes/ModelDefinitionValidator.html +16 -0
- package/docs/classes/NotImplementedError.html +14 -0
- package/docs/classes/OperatorClauseTool.html +72 -0
- package/docs/classes/OrderClauseTool.html +20 -0
- package/docs/classes/PrimaryKeysDefinitionValidator.html +16 -0
- package/docs/classes/PropertiesDefinitionValidator.html +16 -0
- package/docs/classes/ReferencesManyResolver.html +16 -0
- package/docs/classes/RelationsDefinitionValidator.html +16 -0
- package/docs/classes/Repository.html +44 -0
- package/docs/classes/RepositoryRegistry.html +18 -0
- package/docs/classes/Schema.html +20 -0
- package/docs/classes/SliceClauseTool.html +20 -0
- package/docs/classes/WhereClauseTool.html +18 -0
- package/docs/enums/DataType.html +8 -0
- package/docs/enums/RelationType.html +6 -0
- package/docs/functions/capitalize.html +2 -0
- package/docs/functions/cloneDeep.html +2 -0
- package/docs/functions/excludeObjectKeys.html +2 -0
- package/docs/functions/getCtorName.html +2 -0
- package/docs/functions/getValueByPath.html +2 -0
- package/docs/functions/isCtor.html +2 -0
- package/docs/functions/isPureObject.html +2 -0
- package/docs/functions/selectObjectKeys.html +2 -0
- package/docs/functions/singularize.html +2 -0
- package/docs/functions/stringToRegexp.html +2 -0
- package/docs/index.html +284 -0
- package/docs/interfaces/AndClause.html +5 -0
- package/docs/interfaces/OrClause.html +5 -0
- package/docs/modules.html +80 -0
- package/docs/types/AnyObject.html +2 -0
- package/docs/types/BelongsToDefinition.html +6 -0
- package/docs/types/DEFAULT_PRIMARY_KEY_PROPERTY_NAME.html +2 -0
- package/docs/types/DatasourceDefinition.html +2 -0
- package/docs/types/FieldsClause.html +4 -0
- package/docs/types/FilterClause.html +2 -0
- package/docs/types/Flatten.html +1 -0
- package/docs/types/FullPropertyDefinition.html +2 -0
- package/docs/types/HasManyDefinition.html +4 -0
- package/docs/types/HasOneDefinition.html +4 -0
- package/docs/types/Identity.html +2 -0
- package/docs/types/IncludeClause.html +14 -0
- package/docs/types/ItemFilterClause.html +2 -0
- package/docs/types/ModelData.html +2 -0
- package/docs/types/ModelDefinition.html +2 -0
- package/docs/types/ModelId.html +2 -0
- package/docs/types/NestedIncludeClause.html +10 -0
- package/docs/types/NormalizedFieldsClause.html +4 -0
- package/docs/types/NormalizedIncludeClause.html +6 -0
- package/docs/types/OperatorClause.html +4 -0
- package/docs/types/OptionalUnlessRequiredId.html +2 -0
- package/docs/types/OrderClause.html +4 -0
- package/docs/types/PartialBy.html +2 -0
- package/docs/types/PartialWithoutId.html +2 -0
- package/docs/types/PolyBelongsToDefinition.html +6 -0
- package/docs/types/PolyHasManyDefinitionWithTargetKeys.html +4 -0
- package/docs/types/PolyHasManyDefinitionWithTargetRelationName.html +4 -0
- package/docs/types/PolyHasOneDefinitionWithTargetKeys.html +4 -0
- package/docs/types/PolyHasOneDefinitionWithTargetRelationName.html +4 -0
- package/docs/types/PropertiesClause.html +4 -0
- package/docs/types/PropertyDefinition.html +2 -0
- package/docs/types/PropertyDefinitionMap.html +2 -0
- package/docs/types/ReferencesManyDefinition.html +6 -0
- package/docs/types/RelationDefinition.html +4 -0
- package/docs/types/RelationDefinitionMap.html +2 -0
- package/docs/types/WhereClause.html +4 -0
- package/docs/types/WithoutId.html +2 -0
- package/package.json +14 -12
- package/src/adapter/adapter.d.ts +13 -0
- package/src/adapter/adapter.js +15 -0
- package/src/adapter/adapter.spec.js +8 -0
- package/src/adapter/builtin/memory-adapter.d.ts +13 -0
- package/src/adapter/builtin/memory-adapter.js +30 -0
- package/src/adapter/builtin/memory-adapter.spec.js +660 -8
- package/src/adapter/decorator/data-sanitizing-decorator.js +6 -0
- package/src/adapter/decorator/data-sanitizing-decorator.spec.js +13 -0
- package/src/adapter/decorator/data-validation-decorator.js +6 -0
- package/src/adapter/decorator/data-validation-decorator.spec.js +13 -0
- package/src/adapter/decorator/default-values-decorator.js +6 -0
- package/src/adapter/decorator/default-values-decorator.spec.js +16 -0
- package/src/adapter/decorator/fields-filtering-decorator.js +13 -0
- package/src/adapter/decorator/fields-filtering-decorator.spec.js +17 -0
- package/src/adapter/decorator/inclusion-decorator.js +13 -0
- package/src/adapter/decorator/inclusion-decorator.spec.js +17 -0
- package/src/definition/model/relations/relation-definition.d.ts +11 -29
- package/src/definition/model/relations/relations-definition-validator.js +4 -4
- package/src/definition/model/relations/relations-definition-validator.spec.js +18 -18
- package/src/filter/filter-clause.d.ts +5 -4
- package/src/repository/repository.js +2 -6
- package/typedoc.json +4 -0
package/README.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
## @e22m4u/js-repository
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
|
36
|
-
|
|
37
|
-
A[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
137
|
+
const schema = new Schema();
|
|
138
|
+
```
|
|
79
139
|
|
|
80
|
-
|
|
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: '
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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.
|
|
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
|
-
|
|
138
|
-
- `DataType.STRING`
|
|
139
|
-
- `DataType.NUMBER`
|
|
140
|
-
- `DataType.BOOLEAN`
|
|
141
|
-
- `DataType.ARRAY`
|
|
142
|
-
- `DataType.OBJECT`
|
|
173
|
+
**Параметры**
|
|
143
174
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
175
|
+
- `name: string` уникальное название
|
|
176
|
+
- `adapter: string` выбранный адаптер
|
|
177
|
+
- параметры адаптера (если имеются)
|
|
178
|
+
|
|
179
|
+
**Примеры**
|
|
180
|
+
|
|
181
|
+
Определение нового источника.
|
|
148
182
|
|
|
149
183
|
```js
|
|
150
|
-
schema.
|
|
151
|
-
name: '
|
|
152
|
-
|
|
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
|
-
|
|
164
|
-
значения поля `location`. Возможный документ данной коллекции может
|
|
165
|
-
выглядеть так:
|
|
190
|
+
Передача дополнительных параметров адаптера.
|
|
166
191
|
|
|
167
|
-
```
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
"
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
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: '
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
203
|
-
`myMemory`, который был объявлен ранее. Наличие источника позволяет получить
|
|
204
|
-
репозиторий по названию модели.
|
|
259
|
+
Краткое определение свойств модели.
|
|
205
260
|
|
|
206
261
|
```js
|
|
207
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
Добавленный в базу документ можно полностью заменить зная его идентификатор.
|
|
254
|
-
Воспользуемся методом `replaceById`, который перезапишет данные по значению
|
|
255
|
-
первичного ключа.
|
|
289
|
+
Фабричное значение по умолчанию. Возвращаемое значение функции будет
|
|
290
|
+
определено в момент записи документа.
|
|
256
291
|
|
|
257
292
|
```js
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
309
|
+
## Репозиторий
|
|
280
310
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
не затрагивая другие данные.
|
|
311
|
+
Выполняет операции чтения и записи документов определенной модели.
|
|
312
|
+
Получить репозиторий можно методом `getRepository` экземпляра схемы.
|
|
284
313
|
|
|
285
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
331
|
+
- `id: number|string` идентификатор (первичный ключ)
|
|
332
|
+
- `data: object` объект отражающий состав документа
|
|
333
|
+
- `where: object` параметры выборки (см. [Фильтрация](#Фильтрация))
|
|
334
|
+
- `filter: object` параметры возвращаемого результата (см. [Фильтрация](#Фильтрация))
|
|
308
335
|
|
|
309
|
-
|
|
310
|
-
schema.defineModel({
|
|
311
|
-
name: 'user', // название модели
|
|
312
|
-
adapter: 'myMemory', // выбранный источник
|
|
313
|
-
properties: { // поля модели
|
|
314
|
-
name: 'string',
|
|
315
|
-
age: 'number',
|
|
316
|
-
},
|
|
317
|
-
});
|
|
318
|
-
```
|
|
336
|
+
**Примеры**
|
|
319
337
|
|
|
320
|
-
|
|
338
|
+
Получение репозитория по названию модели.
|
|
321
339
|
|
|
322
340
|
```js
|
|
323
|
-
const
|
|
341
|
+
const countryRep = schema.getRepository('country');
|
|
324
342
|
```
|
|
325
343
|
|
|
326
|
-
|
|
344
|
+
Добавление нового документа в коллекцию.
|
|
327
345
|
|
|
328
346
|
```js
|
|
329
|
-
const
|
|
330
|
-
name: '
|
|
331
|
-
|
|
347
|
+
const res = await countryRep.create({
|
|
348
|
+
name: 'Russia',
|
|
349
|
+
population: 143400000,
|
|
332
350
|
});
|
|
333
351
|
|
|
334
|
-
console.log(
|
|
352
|
+
console.log(res);
|
|
335
353
|
// {
|
|
336
|
-
// id: 1,
|
|
337
|
-
// name:
|
|
338
|
-
//
|
|
354
|
+
// "id": 1,
|
|
355
|
+
// "name": "Russia",
|
|
356
|
+
// "population": 143400000,
|
|
339
357
|
// }
|
|
340
358
|
```
|
|
341
359
|
|
|
342
|
-
|
|
360
|
+
Поиск документа по идентификатору.
|
|
343
361
|
|
|
344
362
|
```js
|
|
345
|
-
const
|
|
346
|
-
fedor.id,
|
|
347
|
-
{age: 30},
|
|
348
|
-
);
|
|
363
|
+
const res = await countryRep.findById(1);
|
|
349
364
|
|
|
350
|
-
console.log(
|
|
365
|
+
console.log(res);
|
|
351
366
|
// {
|
|
352
|
-
// id: 1,
|
|
353
|
-
// name:
|
|
354
|
-
//
|
|
367
|
+
// "id": 1,
|
|
368
|
+
// "name": "Russia",
|
|
369
|
+
// "population": 143400000,
|
|
355
370
|
// }
|
|
356
371
|
```
|
|
357
372
|
|
|
358
|
-
|
|
373
|
+
Удаление документа по идентификатору.
|
|
359
374
|
|
|
360
375
|
```js
|
|
361
|
-
await
|
|
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
|
-
|
|
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
|
-
|
|
386
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
473
|
-
- `adapter: string` выбранный адаптер базы данных
|
|
449
|
+
**Примеры**
|
|
474
450
|
|
|
475
|
-
|
|
451
|
+
Упорядочить по полю `createdAt`
|
|
476
452
|
|
|
477
453
|
```js
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
506
|
-
- `datasource: string` выбранный источник данных
|
|
507
|
-
- `properties: object` определения полей модели
|
|
508
|
-
- `relations: object` определения связей модели
|
|
487
|
+
**Примеры**
|
|
509
488
|
|
|
510
|
-
|
|
489
|
+
Включение связи по названию.
|
|
511
490
|
|
|
512
491
|
```js
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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`
|
|
549
|
+
- `discriminator: string` свойство текущей модели для названия целевой*
|
|
550
|
+
|
|
551
|
+
*i. Полиморфный режим позволяет динамически определять целевую модель
|
|
552
|
+
по ее названию, которое хранит документ в свойстве-дискриминаторе.*
|
|
621
553
|
|
|
622
|
-
|
|
623
|
-
где имя целевой модели хранится в отдельном поле, рядом с `foreignKey`*
|
|
554
|
+
**Тип связи**
|
|
624
555
|
|
|
625
|
-
|
|
556
|
+
- `belongsTo` - текущая модель содержит свойство для идентификатора цели
|
|
557
|
+
- `hasOne` - обратная сторона `belongsTo` по принципу "один к одному"
|
|
558
|
+
- `hasMany` - обратная сторона `belongsTo` по принципу "один ко многим"
|
|
559
|
+
- `referencesMany` - документ содержит массив с идентификаторами целевой модели
|
|
626
560
|
|
|
627
|
-
|
|
561
|
+
**Примеры**
|
|
562
|
+
|
|
563
|
+
Объявление связи `belongsTo`
|
|
628
564
|
|
|
629
565
|
```js
|
|
630
566
|
schema.defineModel({
|
|
631
|
-
|
|
567
|
+
name: 'user',
|
|
632
568
|
relations: {
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
foreignKey
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
Связь покупателя к заказу, как обратная сторона `belongsTo`
|
|
596
|
+
Объявление связи `referencesMany`
|
|
663
597
|
|
|
664
598
|
```js
|
|
665
599
|
schema.defineModel({
|
|
666
|
-
|
|
600
|
+
name: 'article',
|
|
667
601
|
relations: {
|
|
668
|
-
//
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
foreignKey
|
|
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
|
-
|
|
614
|
+
Полиморфная версия `belongsTo`
|
|
679
615
|
|
|
680
616
|
```js
|
|
681
617
|
schema.defineModel({
|
|
682
|
-
|
|
618
|
+
name: 'file',
|
|
683
619
|
relations: {
|
|
684
|
-
//
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
|
|
633
|
+
Полиморфная версия `belongsTo` с указанием свойств.
|
|
695
634
|
|
|
696
635
|
```js
|
|
697
636
|
schema.defineModel({
|
|
698
|
-
|
|
637
|
+
name: 'file',
|
|
699
638
|
relations: {
|
|
700
|
-
//
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
-
|
|
713
|
-
|
|
714
|
-
Связь покупателя к заказам, как обратная сторона `belongsTo`
|
|
649
|
+
Полиморфная версия `hasMany` с указанием названия связи целевой модели.
|
|
715
650
|
|
|
716
651
|
```js
|
|
717
652
|
schema.defineModel({
|
|
718
|
-
|
|
653
|
+
name: 'letter',
|
|
719
654
|
relations: {
|
|
720
|
-
//
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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
|
-
|
|
664
|
+
Полиморфная версия `hasMany` с указанием свойств целевой модели.
|
|
731
665
|
|
|
732
666
|
```js
|
|
733
667
|
schema.defineModel({
|
|
734
|
-
|
|
668
|
+
name: 'letter',
|
|
735
669
|
relations: {
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
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
|
-
|
|
681
|
+
## Расширение
|
|
682
|
+
|
|
683
|
+
Метод `getRepository` экземпляра схемы проверяет наличие существующего
|
|
684
|
+
репозитория для указанной модели и возвращает его. В противном случае
|
|
685
|
+
создается новый экземпляр, который будет сохранен для последующих
|
|
686
|
+
обращений к методу.
|
|
747
687
|
|
|
748
688
|
```js
|
|
749
|
-
|
|
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
|
-
|
|
692
|
+
// const schema = new Schema();
|
|
693
|
+
// schema.defineDatasource ...
|
|
694
|
+
// schema.defineModel ...
|
|
765
695
|
|
|
766
|
-
|
|
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
|
-
|
|
774
|
-
|
|
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
|
## Тесты
|