@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acodeninja/persist",
3
- "version": "3.0.0-next.2",
3
+ "version": "3.0.0-next.4",
4
4
  "description": "A JSON based data modelling and persistence module with alternate storage mechanisms.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -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)) return;
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)) throw new ModelNotRegisteredStorageEngineError(modelToProcess, this);
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 = (JSON.stringify(currentModel?.toData() || {}) !== JSON.stringify(modelToProcess.toData()));
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
- this._indexedFieldsHaveChanged(currentModel, modelToProcess)
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(modelsToPut.map(m => this._putModel(m.toData())));
61
- await Promise.all(Object.entries(modelsToReindex).map(async ([constructor, models]) => {
62
- const modelConstructor = this.models[constructor];
63
- const index = await this._getIndex(modelConstructor);
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
- await this._putIndex(modelConstructor, {
66
- ...index || {},
67
- ...Object.fromEntries(models.map(m => [m.id, m.toIndexData()])),
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
- * Decide if two models indexable fields have changed
74
- * @param {Type.Model} currentModel
75
- * @param {Type.Model} modelToProcess
76
- * @return {boolean}
77
- * @private
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
- * @return {Promise<void>}
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<void>
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
  /**