@acodeninja/persist 3.0.0-next.2 → 3.0.0-next.4
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/package.json +1 -1
- package/src/engine/StorageEngine.js +126 -29
package/package.json
CHANGED
@@ -20,34 +20,46 @@ export default class StorageEngine {
|
|
20
20
|
const processedModels = [];
|
21
21
|
const modelsToPut = [];
|
22
22
|
const modelsToReindex = {};
|
23
|
+
const modelsToReindexSearch = {};
|
23
24
|
|
24
25
|
/**
|
25
26
|
* @param {Type.Model} modelToProcess
|
26
27
|
* @return {Promise<void>}
|
27
28
|
*/
|
28
29
|
const processModel = async (modelToProcess) => {
|
29
|
-
if (processedModels.includes(modelToProcess.id))
|
30
|
+
if (processedModels.includes(modelToProcess.id))
|
31
|
+
return;
|
30
32
|
|
31
33
|
processedModels.push(modelToProcess.id);
|
32
34
|
|
33
|
-
if (!Object.keys(this.models).includes(modelToProcess.constructor.name))
|
35
|
+
if (!Object.keys(this.models).includes(modelToProcess.constructor.name))
|
36
|
+
throw new ModelNotRegisteredStorageEngineError(modelToProcess, this);
|
34
37
|
|
35
38
|
modelToProcess.validate();
|
36
39
|
const currentModel = await this.get(modelToProcess.id).catch(() => null);
|
37
40
|
|
38
|
-
const modelToProcessHasChanged =
|
41
|
+
const modelToProcessHasChanged = JSON.stringify(currentModel?.toData() || {}) !== JSON.stringify(modelToProcess.toData());
|
39
42
|
|
40
43
|
if (modelToProcessHasChanged) modelsToPut.push(modelToProcess);
|
41
44
|
|
42
45
|
if (
|
43
46
|
Boolean(modelToProcess.constructor.indexedProperties().length) &&
|
44
|
-
|
47
|
+
indexedFieldsHaveChanged(currentModel, modelToProcess)
|
45
48
|
) {
|
46
49
|
const modelToProcessConstructor = this.getModelConstructorFromId(modelToProcess.id);
|
47
50
|
modelsToReindex[modelToProcessConstructor] = modelsToReindex[modelToProcessConstructor] || [];
|
48
51
|
modelsToReindex[modelToProcessConstructor].push(modelToProcess);
|
49
52
|
}
|
50
53
|
|
54
|
+
if (
|
55
|
+
Boolean(modelToProcess.constructor.searchProperties().length) &&
|
56
|
+
searchableFieldsHaveChanged(currentModel, modelToProcess)
|
57
|
+
) {
|
58
|
+
const modelToProcessConstructor = this.getModelConstructorFromId(modelToProcess.id);
|
59
|
+
modelsToReindexSearch[modelToProcessConstructor] = modelsToReindexSearch[modelToProcessConstructor] || [];
|
60
|
+
modelsToReindexSearch[modelToProcessConstructor].push(modelToProcess);
|
61
|
+
}
|
62
|
+
|
51
63
|
for (const [field, value] of Object.entries(modelToProcess)) {
|
52
64
|
if (Type.Model.isModel(value)) {
|
53
65
|
await processModel(modelToProcess[field]);
|
@@ -57,37 +69,34 @@ export default class StorageEngine {
|
|
57
69
|
|
58
70
|
await processModel(model);
|
59
71
|
|
60
|
-
await Promise.all(
|
61
|
-
|
62
|
-
|
63
|
-
|
72
|
+
await Promise.all([
|
73
|
+
Promise.all(modelsToPut.map(m => this._putModel(m.toData()))),
|
74
|
+
Promise.all(Object.entries(modelsToReindex).map(async ([constructorName, models]) => {
|
75
|
+
const modelConstructor = this.models[constructorName];
|
76
|
+
const index = await this._getIndex(modelConstructor);
|
64
77
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
78
|
+
await this._putIndex(modelConstructor, {
|
79
|
+
...index || {},
|
80
|
+
...Object.fromEntries(models.map(m => [m.id, m.toIndexData()])),
|
81
|
+
});
|
82
|
+
})),
|
83
|
+
Promise.all(Object.entries(modelsToReindexSearch).map(async ([constructorName, models]) => {
|
84
|
+
const modelConstructor = this.models[constructorName];
|
85
|
+
const index = await this._getSearchIndex(modelConstructor);
|
71
86
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
*/
|
79
|
-
_indexedFieldsHaveChanged(currentModel, modelToProcess) {
|
80
|
-
return !currentModel || Boolean(
|
81
|
-
modelToProcess.constructor.indexedProperties()
|
82
|
-
.filter(field => JSON.stringify(_.get(currentModel, field)) !== JSON.stringify(_.get(modelToProcess, field)))
|
83
|
-
.length,
|
84
|
-
);
|
87
|
+
await this._putSearchIndex(modelConstructor, {
|
88
|
+
...index || {},
|
89
|
+
...Object.fromEntries(models.map(m => [m.id, m.toIndexData()])),
|
90
|
+
});
|
91
|
+
})),
|
92
|
+
]);
|
85
93
|
}
|
86
94
|
|
87
95
|
/**
|
88
96
|
* Get a model by its id
|
89
97
|
* @param {string} modelId
|
90
|
-
* @
|
98
|
+
* @throws {ModelNotFoundStorageEngineError}
|
99
|
+
* @return {Promise<Type.Model>}
|
91
100
|
*/
|
92
101
|
get(modelId) {
|
93
102
|
try {
|
@@ -98,6 +107,21 @@ export default class StorageEngine {
|
|
98
107
|
return this._getModel(modelId);
|
99
108
|
}
|
100
109
|
|
110
|
+
/**
|
111
|
+
*
|
112
|
+
* @param {Type.Model} model
|
113
|
+
* @throws {ModelNotRegisteredStorageEngineError}
|
114
|
+
* @throws {ModelNotFoundStorageEngineError}
|
115
|
+
*/
|
116
|
+
async delete(model) {
|
117
|
+
if (!Object.keys(this.models).includes(model.constructor.name))
|
118
|
+
throw new ModelNotRegisteredStorageEngineError(model, this);
|
119
|
+
|
120
|
+
const currentModel = await this.get(model.id);
|
121
|
+
|
122
|
+
await this._deleteModel(currentModel.id);
|
123
|
+
}
|
124
|
+
|
101
125
|
/**
|
102
126
|
* Get the model constructor from a model id
|
103
127
|
* @param {string} modelId
|
@@ -172,12 +196,23 @@ export default class StorageEngine {
|
|
172
196
|
* @param {string} _id
|
173
197
|
* @throws MethodNotImplementedStorageEngineError
|
174
198
|
* @throws ModelNotFoundStorageEngineError
|
175
|
-
* @return Promise<
|
199
|
+
* @return Promise<Model>
|
176
200
|
*/
|
177
201
|
_getModel(_id) {
|
178
202
|
return Promise.reject(new MethodNotImplementedStorageEngineError('_getModel', this));
|
179
203
|
}
|
180
204
|
|
205
|
+
/**
|
206
|
+
* Delete a model
|
207
|
+
* @param {string} _id
|
208
|
+
* @throws MethodNotImplementedStorageEngineError
|
209
|
+
* @throws ModelNotFoundStorageEngineError
|
210
|
+
* @return Promise<void>
|
211
|
+
*/
|
212
|
+
_deleteModel(_id) {
|
213
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_deleteModel', this));
|
214
|
+
}
|
215
|
+
|
181
216
|
/**
|
182
217
|
* Get a model's index data
|
183
218
|
* @param {Model.constructor} _modelConstructor
|
@@ -198,6 +233,68 @@ export default class StorageEngine {
|
|
198
233
|
_putIndex(_modelConstructor, _data) {
|
199
234
|
return Promise.reject(new MethodNotImplementedStorageEngineError('_putIndex', this));
|
200
235
|
}
|
236
|
+
|
237
|
+
/**
|
238
|
+
* Get a model's raw search index data
|
239
|
+
* @param {Model.constructor} _modelConstructor
|
240
|
+
* @throws MethodNotImplementedStorageEngineError
|
241
|
+
* @return Promise<void>
|
242
|
+
*/
|
243
|
+
_getSearchIndex(_modelConstructor) {
|
244
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_getSearchIndex', this));
|
245
|
+
}
|
246
|
+
|
247
|
+
/**
|
248
|
+
* Get a model's raw search index data
|
249
|
+
* @param {Model.constructor} _modelConstructor
|
250
|
+
* @throws MethodNotImplementedStorageEngineError
|
251
|
+
* @return Promise<void>
|
252
|
+
*/
|
253
|
+
_getSearchIndexCompiled(_modelConstructor) {
|
254
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_getSearchIndexCompiled', this));
|
255
|
+
}
|
256
|
+
|
257
|
+
/**
|
258
|
+
* Put a model's raw and compiled search index data
|
259
|
+
* @param {Model.constructor} _modelConstructor
|
260
|
+
* @param {object} _data
|
261
|
+
* @throws MethodNotImplementedStorageEngineError
|
262
|
+
* @return Promise<void>
|
263
|
+
*/
|
264
|
+
_putSearchIndex(_modelConstructor, _data) {
|
265
|
+
return Promise.reject(new MethodNotImplementedStorageEngineError('_putSearchIndex', this));
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Decide if two models indexable fields have changed
|
272
|
+
* @param {Type.Model} currentModel
|
273
|
+
* @param {Type.Model} modelToProcess
|
274
|
+
* @return {boolean}
|
275
|
+
* @private
|
276
|
+
*/
|
277
|
+
function indexedFieldsHaveChanged(currentModel, modelToProcess) {
|
278
|
+
return !currentModel || Boolean(
|
279
|
+
modelToProcess.constructor.indexedProperties()
|
280
|
+
.filter(field => JSON.stringify(_.get(currentModel, field)) !== JSON.stringify(_.get(modelToProcess, field)))
|
281
|
+
.length,
|
282
|
+
);
|
283
|
+
}
|
284
|
+
|
285
|
+
/**
|
286
|
+
* Decide if two models searchable fields have changed
|
287
|
+
* @param {Type.Model} currentModel
|
288
|
+
* @param {Type.Model} modelToProcess
|
289
|
+
* @return {boolean}
|
290
|
+
* @private
|
291
|
+
*/
|
292
|
+
function searchableFieldsHaveChanged(currentModel, modelToProcess) {
|
293
|
+
return !currentModel || Boolean(
|
294
|
+
modelToProcess.constructor.searchProperties()
|
295
|
+
.filter(field => JSON.stringify(_.get(currentModel, field)) !== JSON.stringify(_.get(modelToProcess, field)))
|
296
|
+
.length,
|
297
|
+
);
|
201
298
|
}
|
202
299
|
|
203
300
|
/**
|