@e22m4u/js-repository 0.0.42 → 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 +515 -577
- 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 +652 -0
- 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 +3 -3
- package/src/definition/model/relations/relations-definition-validator.js +4 -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,638 +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` экземпляра схемы.
|
|
172
|
+
|
|
173
|
+
**Параметры**
|
|
136
174
|
|
|
137
|
-
- `
|
|
138
|
-
- `
|
|
139
|
-
-
|
|
140
|
-
- `DataType.BOOLEAN`
|
|
141
|
-
- `DataType.ARRAY`
|
|
142
|
-
- `DataType.OBJECT`
|
|
175
|
+
- `name: string` уникальное название
|
|
176
|
+
- `adapter: string` выбранный адаптер
|
|
177
|
+
- параметры адаптера (если имеются)
|
|
143
178
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
источник данных в параметре `datasource`
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
В отличие от `latLng`, модель `place` имеет источник данных с названием
|
|
203
|
-
`myMemory`, который был объявлен ранее. Наличие источника позволяет получить
|
|
204
|
-
репозиторий по названию модели.
|
|
205
|
-
|
|
206
|
-
```js
|
|
207
|
-
const rep = schema.getRepository('place');
|
|
208
|
-
```
|
|
209
|
-
|
|
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)`
|
|
257
|
+
**Примеры**
|
|
224
258
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Создадим торговую точку методом `create` используя репозиторий из примера
|
|
228
|
-
выше. Метод возвращает документ, который был записан в базу, включая присвоенный
|
|
229
|
-
идентификатор.
|
|
259
|
+
Краткое определение свойств модели.
|
|
230
260
|
|
|
231
261
|
```js
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
262
|
+
schema.defineModel({
|
|
263
|
+
name: 'city',
|
|
264
|
+
properties: { // свойства модели
|
|
265
|
+
name: DataType.STRING, // тип свойства "string"
|
|
266
|
+
population: DataType.NUMBER, // тип свойства "number"
|
|
237
267
|
},
|
|
238
268
|
});
|
|
239
|
-
|
|
240
|
-
console.log(place);
|
|
241
|
-
// {
|
|
242
|
-
// "id": 1,
|
|
243
|
-
// "name": "Burger King",
|
|
244
|
-
// "location": {
|
|
245
|
-
// "lat": 32.412891,
|
|
246
|
-
// "lng": 34.7660061
|
|
247
|
-
// }
|
|
248
|
-
// }
|
|
249
269
|
```
|
|
250
270
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
Добавленный в базу документ можно полностью заменить зная его идентификатор.
|
|
254
|
-
Воспользуемся методом `replaceById`, который перезапишет данные по значению
|
|
255
|
-
первичного ключа.
|
|
271
|
+
Расширенное определение свойств модели.
|
|
256
272
|
|
|
257
273
|
```js
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
+
},
|
|
285
|
+
},
|
|
269
286
|
});
|
|
270
|
-
|
|
271
|
-
console.log(result);
|
|
272
|
-
// {
|
|
273
|
-
// "id": 1,
|
|
274
|
-
// "name": "Starbucks",
|
|
275
|
-
// "address": "Sukhumvit Alley"
|
|
276
|
-
// }
|
|
277
287
|
```
|
|
278
288
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
Если вы знакомы с методом PUT в архитектуре REST, когда документ
|
|
282
|
-
добавляется, если его не существовало, или же обновляется существующий,
|
|
283
|
-
то `replaceOrCreate` работает схожим образом. Если параметр
|
|
284
|
-
`data` передаваемый первым аргументом будет содержать идентификатор,
|
|
285
|
-
то метод будет вести себя как `replaceById`, в противном случае будет
|
|
286
|
-
создан новый документ.
|
|
289
|
+
Фабричное значение по умолчанию. Возвращаемое значение функции будет
|
|
290
|
+
определено в момент записи документа.
|
|
287
291
|
|
|
288
292
|
```js
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
+
},
|
|
299
306
|
});
|
|
300
|
-
|
|
301
|
-
console.log(result);
|
|
302
|
-
// {
|
|
303
|
-
// "id": 1,
|
|
304
|
-
// "name": "Airport",
|
|
305
|
-
// "city": "Antalya"
|
|
306
|
-
// "code": "AYT"
|
|
307
|
-
// }
|
|
308
307
|
```
|
|
309
308
|
|
|
310
|
-
|
|
311
|
-
замены существующего документа. Теперь рассмотрим создание
|
|
312
|
-
документа с новым идентификатором.
|
|
309
|
+
## Репозиторий
|
|
313
310
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
name: 'Airport',
|
|
317
|
-
city: 'Bangkok',
|
|
318
|
-
code: 'BKK',
|
|
319
|
-
});
|
|
311
|
+
Выполняет операции чтения и записи документов определенной модели.
|
|
312
|
+
Получить репозиторий можно методом `getRepository` экземпляра схемы.
|
|
320
313
|
|
|
321
|
-
|
|
322
|
-
// {
|
|
323
|
-
// "id": 2,
|
|
324
|
-
// "name": "Airport",
|
|
325
|
-
// "city": "Bangkok",
|
|
326
|
-
// "code": "BKK"
|
|
327
|
-
// }
|
|
328
|
-
```
|
|
314
|
+
**Методы**
|
|
329
315
|
|
|
330
|
-
|
|
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)` подсчет всех документов или по условию
|
|
331
328
|
|
|
332
|
-
|
|
333
|
-
не были переданы, что позволяет обновить только часть документа,
|
|
334
|
-
не затрагивая другие данные.
|
|
329
|
+
**Аргументы**
|
|
335
330
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
// "city": "Bangkok",
|
|
341
|
-
// "code": "BKK"
|
|
342
|
-
// }
|
|
343
|
-
const result = rep.patchById(place.id, {
|
|
344
|
-
city: 'Moscow',
|
|
345
|
-
code: 'SVO'
|
|
346
|
-
});
|
|
331
|
+
- `id: number|string` идентификатор (первичный ключ)
|
|
332
|
+
- `data: object` объект отражающий состав документа
|
|
333
|
+
- `where: object` параметры выборки (см. [Фильтрация](#Фильтрация))
|
|
334
|
+
- `filter: object` параметры возвращаемого результата (см. [Фильтрация](#Фильтрация))
|
|
347
335
|
|
|
348
|
-
|
|
349
|
-
// {
|
|
350
|
-
// "id": 2,
|
|
351
|
-
// "name": "Airport",
|
|
352
|
-
// "city": "Moscow",
|
|
353
|
-
// "code": "SVO"
|
|
354
|
-
// }
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## Пример
|
|
336
|
+
**Примеры**
|
|
358
337
|
|
|
359
|
-
|
|
338
|
+
Получение репозитория по названию модели.
|
|
360
339
|
|
|
361
340
|
```js
|
|
362
|
-
schema.
|
|
363
|
-
name: 'user', // название модели
|
|
364
|
-
adapter: 'myMemory', // выбранный источник
|
|
365
|
-
properties: { // поля модели
|
|
366
|
-
name: 'string',
|
|
367
|
-
age: 'number',
|
|
368
|
-
},
|
|
369
|
-
});
|
|
341
|
+
const countryRep = schema.getRepository('country');
|
|
370
342
|
```
|
|
371
343
|
|
|
372
|
-
|
|
344
|
+
Добавление нового документа в коллекцию.
|
|
373
345
|
|
|
374
346
|
```js
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
Добавляем новую запись методом `create`
|
|
379
|
-
|
|
380
|
-
```js
|
|
381
|
-
const fedor = await userRep.create({
|
|
382
|
-
name: 'Fedor',
|
|
383
|
-
age: 24,
|
|
347
|
+
const res = await countryRep.create({
|
|
348
|
+
name: 'Russia',
|
|
349
|
+
population: 143400000,
|
|
384
350
|
});
|
|
385
351
|
|
|
386
|
-
console.log(
|
|
352
|
+
console.log(res);
|
|
387
353
|
// {
|
|
388
|
-
// id: 1,
|
|
389
|
-
// name:
|
|
390
|
-
//
|
|
354
|
+
// "id": 1,
|
|
355
|
+
// "name": "Russia",
|
|
356
|
+
// "population": 143400000,
|
|
391
357
|
// }
|
|
392
358
|
```
|
|
393
359
|
|
|
394
|
-
|
|
360
|
+
Поиск документа по идентификатору.
|
|
395
361
|
|
|
396
362
|
```js
|
|
397
|
-
const
|
|
398
|
-
fedor.id,
|
|
399
|
-
{age: 30},
|
|
400
|
-
);
|
|
363
|
+
const res = await countryRep.findById(1);
|
|
401
364
|
|
|
402
|
-
console.log(
|
|
365
|
+
console.log(res);
|
|
403
366
|
// {
|
|
404
|
-
// id: 1,
|
|
405
|
-
// name:
|
|
406
|
-
//
|
|
367
|
+
// "id": 1,
|
|
368
|
+
// "name": "Russia",
|
|
369
|
+
// "population": 143400000,
|
|
407
370
|
// }
|
|
408
371
|
```
|
|
409
372
|
|
|
410
|
-
|
|
373
|
+
Удаление документа по идентификатору.
|
|
411
374
|
|
|
412
375
|
```js
|
|
413
|
-
await
|
|
376
|
+
const res = await countryRep.deleteById(1);
|
|
377
|
+
|
|
378
|
+
console.log(res); // true
|
|
414
379
|
```
|
|
415
380
|
|
|
416
|
-
##
|
|
381
|
+
## Фильтрация
|
|
417
382
|
|
|
418
|
-
|
|
383
|
+
Некоторые методы репозитория принимают объект настроек влияющий
|
|
384
|
+
на возвращаемый результат. Максимально широкий набор таких настроек
|
|
385
|
+
имеет первый параметр метода `find`, где ожидается объект содержащий
|
|
386
|
+
набор опций указанных ниже.
|
|
419
387
|
|
|
420
|
-
|
|
388
|
+
- `where: object` объект выборки
|
|
389
|
+
- `order: string[]` указание порядка
|
|
390
|
+
- `limit: number` ограничение количества документов
|
|
391
|
+
- `skip: number` пропуск документов
|
|
392
|
+
- `fields: string[]` выбор необходимых свойств модели
|
|
393
|
+
- `include: object` включение связанных данных в результат
|
|
421
394
|
|
|
422
|
-
|
|
423
|
-
- `replaceById(id, data, filter = undefined)`
|
|
424
|
-
- `replaceOrCreate(data, filter = undefined)`
|
|
425
|
-
- `patch(data, where = undefined)`
|
|
426
|
-
- `patchById(id, data, filter = undefined)`
|
|
427
|
-
- `find(filter = undefined)`
|
|
428
|
-
- `findOne(filter = undefined)`
|
|
429
|
-
- `findById(id, filter = undefined)`
|
|
430
|
-
- `delete(where = undefined)`
|
|
431
|
-
- `deleteById(id)`
|
|
432
|
-
- `exists(id)`
|
|
433
|
-
- `count(where = undefined)`
|
|
395
|
+
### where
|
|
434
396
|
|
|
435
|
-
|
|
397
|
+
Параметр принимает объект с условиями выборки и поддерживает широкий
|
|
398
|
+
набор операторов сравнения.
|
|
436
399
|
|
|
437
|
-
|
|
438
|
-
|
|
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'}}` флаги регулярного выражения
|
|
439
417
|
|
|
440
|
-
|
|
441
|
-
// создаем источник
|
|
442
|
-
schema.defineDatasource({name: 'myDatasource', adapter: 'memory'});
|
|
443
|
-
// создаем модель
|
|
444
|
-
schema.defineModel({name: 'myModel', datasource: 'myDatasource'});
|
|
445
|
-
// получаем репозиторий по названию модели
|
|
446
|
-
const repositorty = schema.getRepository('myModel');
|
|
447
|
-
```
|
|
418
|
+
*i. Условия можно объединять операторами `and`, `or` и `nor`.*
|
|
448
419
|
|
|
449
|
-
|
|
420
|
+
**Примеры**
|
|
450
421
|
|
|
451
|
-
|
|
452
|
-
import {Schema} from '@e22m4u/js-repository';
|
|
453
|
-
import {Repository} from '@e22m4u/js-repository';
|
|
454
|
-
import {RepositoryRegistry} from '@e22m4u/js-repository';
|
|
422
|
+
Применение условий выборки при подсчете документов.
|
|
455
423
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
424
|
+
```js
|
|
425
|
+
const res = await rep.count({
|
|
426
|
+
authorId: 251,
|
|
427
|
+
publishedAt: {
|
|
428
|
+
lte: '2023-12-02T14:00:00.000Z',
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
```
|
|
459
432
|
|
|
460
|
-
|
|
461
|
-
schema.get(RepositoryRegistry).setRepositoryCtor(MyRepository);
|
|
462
|
-
// теперь schema.getRepository(modelName) будет возвращать
|
|
463
|
-
// экземпляр класса MyRepository
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
### Filter
|
|
467
|
-
|
|
468
|
-
Большинство методов репозитория принимают объект `filter` для
|
|
469
|
-
фильтрации возвращаемого ответа. Описание параметров объекта:
|
|
470
|
-
|
|
471
|
-
- **where** *(условия выборки данных из базы)*
|
|
472
|
-
примеры:
|
|
473
|
-
`where: {foo: 'bar'}` поиск по значению поля `foo`
|
|
474
|
-
`where: {foo: {eq: 'bar'}}` оператор равенства `eq`
|
|
475
|
-
`where: {foo: {neq: 'bar'}}` оператор неравенства `neq`
|
|
476
|
-
`where: {foo: {gt: 5}}` оператор "больше" `gt`
|
|
477
|
-
`where: {foo: {lt: 10}}` оператор "меньше" `lt`
|
|
478
|
-
`where: {foo: {gte: 5}}` оператор "больше или равно" `gte`
|
|
479
|
-
`where: {foo: {lte: 10}}` оператор "меньше или равно" `lte`
|
|
480
|
-
`where: {foo: {inq: ['bar', 'baz']}}` равенство одного из значений `inq`
|
|
481
|
-
`where: {foo: {nin: ['bar', 'baz']}}` исключение значений массива `nin`
|
|
482
|
-
`where: {foo: {between: [5, 10]}}` оператор диапазона `between`
|
|
483
|
-
`where: {foo: {exists: true}}` оператор наличия значения `exists`
|
|
484
|
-
`where: {foo: {like: 'bar'}}` оператор поиска подстроки `like`
|
|
485
|
-
`where: {foo: {ilike: 'BaR'}}` регистронезависимая версия `ilike`
|
|
486
|
-
`where: {foo: {nlike: 'bar'}}` оператор исключения подстроки `nlike`
|
|
487
|
-
`where: {foo: {nilike: 'BaR'}}` регистронезависимая версия `nilike`
|
|
488
|
-
`where: {foo: {regexp: 'ba.+'}}` оператор регулярного выражения `regexp`
|
|
489
|
-
`where: {foo: {regexp: 'ba.+', flags: 'i'}}` флаги регулярного выражения
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
- **order** *(упорядочить записи по полю)*
|
|
493
|
-
примеры:
|
|
494
|
-
`order: 'foo'` порядок по полю `foo`
|
|
495
|
-
`order: 'foo ASC'` явное указание порядка
|
|
496
|
-
`order: 'foo DESC'` инвертировать порядок
|
|
497
|
-
`order: ['foo', 'bar ASC', 'baz DESC']` по нескольким полям
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
- **limit** *(не более N записей)*
|
|
501
|
-
примеры:
|
|
502
|
-
`limit: 0` не ограничивать
|
|
503
|
-
`limit: 14` не более 14-и
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
- **skip** *(пропуск первых N записей)*
|
|
507
|
-
примеры:
|
|
508
|
-
`skip: 0` выборка без пропуска
|
|
509
|
-
`skip: 10` пропустить 10 объектов выборки
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
- **include** *(включение связанных данных в результат)*
|
|
513
|
-
примеры:
|
|
514
|
-
`include: 'foo'` включение связи `foo`
|
|
515
|
-
`include: ['foo', 'bar']` включение `foo` и `bar`
|
|
516
|
-
`include: {foo: 'bar'}` включение вложенной связи `foo`
|
|
433
|
+
Применение оператора `or` при удалении документов.
|
|
517
434
|
|
|
518
|
-
|
|
435
|
+
```js
|
|
436
|
+
const res = await rep.delete({
|
|
437
|
+
or: [
|
|
438
|
+
{draft: true},
|
|
439
|
+
{title: {like: 'draft'}},
|
|
440
|
+
],
|
|
441
|
+
});
|
|
442
|
+
```
|
|
519
443
|
|
|
520
|
-
|
|
444
|
+
### order
|
|
521
445
|
|
|
522
|
-
|
|
446
|
+
Параметр упорядочивает выборку по указанным свойствам модели. Обратное
|
|
447
|
+
направление порядка можно задать постфиксом `DESC` в названии свойства.
|
|
523
448
|
|
|
524
|
-
|
|
525
|
-
- `adapter: string` выбранный адаптер базы данных
|
|
449
|
+
**Примеры**
|
|
526
450
|
|
|
527
|
-
|
|
451
|
+
Упорядочить по полю `createdAt`
|
|
528
452
|
|
|
529
453
|
```js
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
adapter: 'memory',
|
|
454
|
+
const res = await rep.find({
|
|
455
|
+
order: 'createdAt',
|
|
533
456
|
});
|
|
534
457
|
```
|
|
535
458
|
|
|
536
|
-
|
|
537
|
-
при определении источника.
|
|
459
|
+
Упорядочить по полю `createdAt` в обратном порядке.
|
|
538
460
|
|
|
539
|
-
|
|
461
|
+
```js
|
|
462
|
+
const res = await rep.find({
|
|
463
|
+
order: 'createdAt DESC',
|
|
464
|
+
});
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Упорядочить по нескольким свойствам в разных направлениях.
|
|
540
468
|
|
|
541
469
|
```js
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
470
|
+
const res = await rep.find({
|
|
471
|
+
order: [
|
|
472
|
+
'title',
|
|
473
|
+
'price ASC',
|
|
474
|
+
'featured DESC',
|
|
475
|
+
],
|
|
548
476
|
});
|
|
549
477
|
```
|
|
550
478
|
|
|
551
|
-
|
|
479
|
+
*i. Направление порядка `ASC` указывать необязательно.*
|
|
552
480
|
|
|
553
|
-
|
|
481
|
+
### include
|
|
554
482
|
|
|
555
|
-
|
|
483
|
+
Параметр включает связанные документы в результат вызываемого метода.
|
|
484
|
+
Названия включаемых связей должны быть определены в текущей модели.
|
|
485
|
+
(см. [Связи](#Связи))
|
|
556
486
|
|
|
557
|
-
|
|
558
|
-
- `datasource: string` выбранный источник данных
|
|
559
|
-
- `properties: object` определения полей модели
|
|
560
|
-
- `relations: object` определения связей модели
|
|
487
|
+
**Примеры**
|
|
561
488
|
|
|
562
|
-
|
|
489
|
+
Включение связи по названию.
|
|
563
490
|
|
|
564
491
|
```js
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
datasource: 'myDatasource',
|
|
568
|
-
properties: {...}, // см. ниже
|
|
569
|
-
relations: {...}, // см. ниже
|
|
492
|
+
const res = await rep.find({
|
|
493
|
+
include: 'city',
|
|
570
494
|
});
|
|
571
495
|
```
|
|
572
496
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
Параметр `properties` описывает набор полей и их настройки.
|
|
576
|
-
|
|
577
|
-
Типы:
|
|
578
|
-
|
|
579
|
-
- `string`
|
|
580
|
-
- `number`
|
|
581
|
-
- `boolean`
|
|
582
|
-
- `array`
|
|
583
|
-
- `object`
|
|
584
|
-
- `any`
|
|
585
|
-
|
|
586
|
-
Пример:
|
|
497
|
+
Включение вложенных связей.
|
|
587
498
|
|
|
588
499
|
```js
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
prop1: 'string',
|
|
593
|
-
prop2: 'number',
|
|
594
|
-
prop3: 'boolean',
|
|
595
|
-
prop4: 'array',
|
|
596
|
-
prop5: 'object',
|
|
597
|
-
prop6: 'any',
|
|
500
|
+
const res = await rep.find({
|
|
501
|
+
include: {
|
|
502
|
+
city: 'country',
|
|
598
503
|
},
|
|
599
504
|
});
|
|
600
505
|
```
|
|
601
506
|
|
|
602
|
-
|
|
507
|
+
Включение нескольких связей массивом.
|
|
603
508
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
509
|
+
```js
|
|
510
|
+
const res = await rep.find({
|
|
511
|
+
include: [
|
|
512
|
+
'city',
|
|
513
|
+
'address',
|
|
514
|
+
'employees'
|
|
515
|
+
],
|
|
516
|
+
});
|
|
517
|
+
```
|
|
612
518
|
|
|
613
|
-
|
|
519
|
+
Использование фильтрации включаемых документов.
|
|
614
520
|
|
|
615
521
|
```js
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
},
|
|
627
|
-
prop3: {
|
|
628
|
-
type: 'number',
|
|
629
|
-
default: 100,
|
|
630
|
-
},
|
|
631
|
-
prop3: {
|
|
632
|
-
type: 'string',
|
|
633
|
-
// фабричное значение
|
|
634
|
-
default: () => new Date().toISOString(),
|
|
635
|
-
},
|
|
636
|
-
prop4: {
|
|
637
|
-
type: 'array',
|
|
638
|
-
itemType: 'string',
|
|
639
|
-
},
|
|
640
|
-
prop5: {
|
|
641
|
-
type: 'object',
|
|
642
|
-
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"
|
|
643
532
|
},
|
|
644
533
|
},
|
|
645
534
|
});
|
|
@@ -647,188 +536,237 @@ schema.defineModel({
|
|
|
647
536
|
|
|
648
537
|
## Связи
|
|
649
538
|
|
|
650
|
-
Параметр `relations`
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
- источник связи
|
|
655
|
-
*- модель в которой определена данная связь*
|
|
656
|
-
- целевая модель
|
|
657
|
-
*- модель на которую ссылается источник связи*
|
|
539
|
+
Параметр `relations` находится в составе определения модели и принимает
|
|
540
|
+
объект, ключ которого является названием связи, а значением объект
|
|
541
|
+
с параметрами.
|
|
658
542
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
- `belongsTo` - ссылка на целевую модель находится в источнике
|
|
662
|
-
- `hasOne` - ссылка на источник находится в целевой модели (one-to-one)
|
|
663
|
-
- `hasMany` - ссылка на источник находится в целевой модели (one-to-many)
|
|
664
|
-
- `referencesMany` - массив ссылок на целевую модель находится в источнике
|
|
665
|
-
|
|
666
|
-
Параметры:
|
|
543
|
+
**Параметры**
|
|
667
544
|
|
|
668
545
|
- `type: string` тип связи
|
|
669
|
-
- `model: string`
|
|
670
|
-
- `foreignKey: string`
|
|
546
|
+
- `model: string` название целевой модели
|
|
547
|
+
- `foreignKey: string` свойство текущей модели для идентификатора цели
|
|
671
548
|
- `polymorphic: boolean|string` объявить связь полиморфной*
|
|
672
|
-
- `discriminator: string`
|
|
549
|
+
- `discriminator: string` свойство текущей модели для названия целевой*
|
|
550
|
+
|
|
551
|
+
*i. Полиморфный режим позволяет динамически определять целевую модель
|
|
552
|
+
по ее названию, которое хранит документ в свойстве-дискриминаторе.*
|
|
673
553
|
|
|
674
|
-
|
|
675
|
-
где имя целевой модели хранится в отдельном поле, рядом с `foreignKey`*
|
|
554
|
+
**Тип связи**
|
|
676
555
|
|
|
677
|
-
|
|
556
|
+
- `belongsTo` - текущая модель содержит свойство для идентификатора цели
|
|
557
|
+
- `hasOne` - обратная сторона `belongsTo` по принципу "один к одному"
|
|
558
|
+
- `hasMany` - обратная сторона `belongsTo` по принципу "один ко многим"
|
|
559
|
+
- `referencesMany` - документ содержит массив с идентификаторами целевой модели
|
|
678
560
|
|
|
679
|
-
|
|
561
|
+
**Примеры**
|
|
562
|
+
|
|
563
|
+
Объявление связи `belongsTo`
|
|
680
564
|
|
|
681
565
|
```js
|
|
682
566
|
schema.defineModel({
|
|
683
|
-
|
|
567
|
+
name: 'user',
|
|
684
568
|
relations: {
|
|
685
|
-
//
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
foreignKey
|
|
569
|
+
role: { // название связи
|
|
570
|
+
type: RelationType.BELONGS_TO, // текущая модель ссылается на целевую
|
|
571
|
+
model: 'role', // название целевой модели
|
|
572
|
+
foreignKey: 'roleId', // внешний ключ (необязательно)
|
|
573
|
+
// если "foreignKey" не указан, то свойство внешнего
|
|
574
|
+
// ключа формируется согласно названию связи
|
|
575
|
+
// с добавлением постфикса "Id"
|
|
690
576
|
},
|
|
691
577
|
},
|
|
692
578
|
});
|
|
693
579
|
```
|
|
694
580
|
|
|
695
|
-
|
|
581
|
+
Объявление связи `hasMany`
|
|
696
582
|
|
|
697
583
|
```js
|
|
698
584
|
schema.defineModel({
|
|
699
|
-
|
|
585
|
+
name: 'role',
|
|
700
586
|
relations: {
|
|
701
|
-
//
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
foreignKey: 'customerId', // опционально
|
|
706
|
-
discriminator: 'customerType', // опционально
|
|
587
|
+
users: { // название связи
|
|
588
|
+
type: RelationType.HAS_MANY, // целевая модель ссылается на текущую
|
|
589
|
+
model: 'user', // название целевой модели
|
|
590
|
+
foreignKey: 'roleId', // внешний ключ из целевой модели на текущую
|
|
707
591
|
},
|
|
708
592
|
},
|
|
709
593
|
});
|
|
710
594
|
```
|
|
711
595
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
Связь покупателя к заказу, как обратная сторона `belongsTo`
|
|
596
|
+
Объявление связи `referencesMany`
|
|
715
597
|
|
|
716
598
|
```js
|
|
717
599
|
schema.defineModel({
|
|
718
|
-
|
|
600
|
+
name: 'article',
|
|
719
601
|
relations: {
|
|
720
|
-
//
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
foreignKey
|
|
602
|
+
categories: { // название связи
|
|
603
|
+
type: RelationType.REFERENCES_MANY, // связь через массив идентификаторов
|
|
604
|
+
model: 'category', // название целевой модели
|
|
605
|
+
foreignKey: 'categoryIds', // внешний ключ (необязательно)
|
|
606
|
+
// если "foreignKey" не указан, то свойство внешнего
|
|
607
|
+
// ключа формируется согласно названию связи
|
|
608
|
+
// с добавлением постфикса "Ids"
|
|
725
609
|
},
|
|
726
610
|
},
|
|
727
611
|
});
|
|
728
612
|
```
|
|
729
613
|
|
|
730
|
-
|
|
614
|
+
Полиморфная версия `belongsTo`
|
|
731
615
|
|
|
732
616
|
```js
|
|
733
617
|
schema.defineModel({
|
|
734
|
-
|
|
618
|
+
name: 'file',
|
|
735
619
|
relations: {
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
620
|
+
reference: { // название связи
|
|
621
|
+
type: RelationType.BELONGS_TO, // текущая модель ссылается на целевую
|
|
622
|
+
// полиморфный режим позволяет хранить название целевой модели
|
|
623
|
+
// в свойстве-дискриминаторе, которое формируется согласно
|
|
624
|
+
// названию связи с постфиксом "Type", и в данном случае
|
|
625
|
+
// название целевой модели хранит "referenceType",
|
|
626
|
+
// а идентификатор документа "referenceId"
|
|
627
|
+
polymorphic: true,
|
|
741
628
|
},
|
|
742
629
|
},
|
|
743
630
|
});
|
|
744
631
|
```
|
|
745
632
|
|
|
746
|
-
|
|
633
|
+
Полиморфная версия `belongsTo` с указанием свойств.
|
|
747
634
|
|
|
748
635
|
```js
|
|
749
636
|
schema.defineModel({
|
|
750
|
-
|
|
637
|
+
name: 'file',
|
|
751
638
|
relations: {
|
|
752
|
-
//
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
foreignKey: 'customerId', // поле целевой модели
|
|
758
|
-
discriminator: 'customerType', // поле целевой модели
|
|
639
|
+
reference: { // название связи
|
|
640
|
+
type: RelationType.BELONGS_TO, // текущая модель ссылается на целевую
|
|
641
|
+
polymorphic: true, // название целевой модели хранит дискриминатор
|
|
642
|
+
foreignKey: 'referenceId', // свойство для идентификатора цели
|
|
643
|
+
discriminator: 'referenceType', // свойство для названия целевой модели
|
|
759
644
|
},
|
|
760
645
|
},
|
|
761
|
-
})
|
|
646
|
+
})
|
|
762
647
|
```
|
|
763
648
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
Связь покупателя к заказам, как обратная сторона `belongsTo`
|
|
649
|
+
Полиморфная версия `hasMany` с указанием названия связи целевой модели.
|
|
767
650
|
|
|
768
651
|
```js
|
|
769
652
|
schema.defineModel({
|
|
770
|
-
|
|
653
|
+
name: 'letter',
|
|
771
654
|
relations: {
|
|
772
|
-
//
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
foreignKey: 'customerId', // поле целевой модели
|
|
655
|
+
attachments: { // название связи
|
|
656
|
+
type: RelationType.HAS_MANY, // целевая модель ссылается на текущую
|
|
657
|
+
model: 'file', // название целевой модели
|
|
658
|
+
polymorphic: 'reference', // название полиморфной связи целевой модели
|
|
777
659
|
},
|
|
778
660
|
},
|
|
779
|
-
})
|
|
661
|
+
})
|
|
780
662
|
```
|
|
781
663
|
|
|
782
|
-
|
|
664
|
+
Полиморфная версия `hasMany` с указанием свойств целевой модели.
|
|
783
665
|
|
|
784
666
|
```js
|
|
785
667
|
schema.defineModel({
|
|
786
|
-
|
|
668
|
+
name: 'letter',
|
|
787
669
|
relations: {
|
|
788
|
-
//
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
670
|
+
attachments: { // название связи
|
|
671
|
+
type: RelationType.HAS_MANY, // целевая модель ссылается на текущую
|
|
672
|
+
model: 'file', // название целевой модели
|
|
673
|
+
polymorphic: true, // название текущей модели находится в дискриминаторе
|
|
674
|
+
foreignKey: 'referenceId', // свойство целевой модели для идентификатора
|
|
675
|
+
discriminator: 'referenceType', // свойство целевой модели для названия текущей
|
|
793
676
|
},
|
|
794
677
|
},
|
|
795
|
-
})
|
|
678
|
+
})
|
|
796
679
|
```
|
|
797
680
|
|
|
798
|
-
|
|
681
|
+
## Расширение
|
|
682
|
+
|
|
683
|
+
Метод `getRepository` экземпляра схемы проверяет наличие существующего
|
|
684
|
+
репозитория для указанной модели и возвращает его. В противном случае
|
|
685
|
+
создается новый экземпляр, который будет сохранен для последующих
|
|
686
|
+
обращений к методу.
|
|
799
687
|
|
|
800
688
|
```js
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
relations: {
|
|
804
|
-
// ...
|
|
805
|
-
orders: {
|
|
806
|
-
type: 'hasMany',
|
|
807
|
-
model: 'order',
|
|
808
|
-
polymorphic: true, // true вместо имени модели
|
|
809
|
-
foreignKey: 'customerId', // поле целевой модели
|
|
810
|
-
discriminator: 'customerType', // поле целевой модели
|
|
811
|
-
},
|
|
812
|
-
},
|
|
813
|
-
});
|
|
814
|
-
```
|
|
689
|
+
import {Schema} from '@e22m4u/js-repository';
|
|
690
|
+
import {Repository} from '@e22m4u/js-repository';
|
|
815
691
|
|
|
816
|
-
|
|
692
|
+
// const schema = new Schema();
|
|
693
|
+
// schema.defineDatasource ...
|
|
694
|
+
// schema.defineModel ...
|
|
695
|
+
|
|
696
|
+
const rep1 = schema.getRepository('model');
|
|
697
|
+
const rep2 = schema.getRepository('model');
|
|
698
|
+
console.log(rep1 === rep2); // true
|
|
699
|
+
```
|
|
817
700
|
|
|
818
|
-
|
|
701
|
+
Подмена стандартного конструктора репозитория выполняется методом
|
|
702
|
+
`setRepositoryCtor` сервиса `RepositoryRegistry`, который находится
|
|
703
|
+
в контейнере экземпляра схемы. После чего все новые репозитории будут
|
|
704
|
+
создаваться указанным конструктором вместо стандартного.
|
|
819
705
|
|
|
820
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"
|
|
821
741
|
schema.defineModel({
|
|
822
|
-
|
|
742
|
+
name: 'city',
|
|
743
|
+
datasource: 'myDatasource',
|
|
744
|
+
properties: {
|
|
745
|
+
title: DataType.STRING,
|
|
746
|
+
timeZone: DataType.STRING,
|
|
747
|
+
},
|
|
823
748
|
relations: {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
model: 'order',
|
|
828
|
-
foreignKey: 'orderIds', // опционально
|
|
749
|
+
country: {
|
|
750
|
+
type: RelationType.BELONGS_TO,
|
|
751
|
+
model: 'country',
|
|
829
752
|
},
|
|
830
753
|
},
|
|
831
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');
|
|
832
770
|
```
|
|
833
771
|
|
|
834
772
|
## Тесты
|