@acodeninja/persist 2.2.1 → 2.2.3
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/.deepsource.toml +10 -0
- package/package.json +1 -1
- package/src/Persist.js +3 -1
- package/src/Query.js +91 -35
- package/src/SchemaCompiler.js +68 -17
- package/src/Transactions.js +80 -2
- package/src/engine/Engine.js +228 -53
- package/src/engine/FileEngine.js +118 -32
- package/src/engine/HTTPEngine.js +156 -34
- package/src/engine/S3Engine.js +132 -34
- package/src/type/Model.js +139 -24
- package/src/type/Type.js +42 -8
- package/src/type/complex/ArrayType.js +54 -1
- package/src/type/complex/CustomType.js +35 -1
- package/src/type/resolved/ResolvedType.js +39 -1
- package/src/type/resolved/SlugType.js +41 -1
- package/src/type/simple/BooleanType.js +16 -1
- package/src/type/simple/DateType.js +28 -1
- package/src/type/simple/NumberType.js +16 -1
- package/src/type/simple/SimpleType.js +11 -1
- package/src/type/simple/StringType.js +16 -1
package/src/engine/Engine.js
CHANGED
@@ -3,43 +3,120 @@ import Type from '../type/index.js';
|
|
3
3
|
import lunr from 'lunr';
|
4
4
|
|
5
5
|
/**
|
6
|
+
* The `Engine` class provides a base interface for implementing data storage and retrieval engines.
|
7
|
+
* It includes methods for handling models, indexes, and search functionality.
|
8
|
+
*
|
6
9
|
* @class Engine
|
7
10
|
*/
|
8
|
-
|
9
|
-
static
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
class Engine {
|
12
|
+
static configuration = undefined;
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Retrieves a model by its ID. This method must be implemented by subclasses.
|
16
|
+
*
|
17
|
+
* @param {string} _id - The ID of the model to retrieve.
|
18
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
19
|
+
* @returns {Promise<Object>} - Returns a promise resolving to the raw data of the requested model.
|
20
|
+
* @abstract
|
21
|
+
*/
|
22
|
+
static getById(_id) {
|
23
|
+
return Promise.reject(new NotImplementedError(`${this.name} must implement .getById()`));
|
13
24
|
}
|
14
25
|
|
15
|
-
|
16
|
-
|
26
|
+
/**
|
27
|
+
* Saves a model to the data store. This method must be implemented by subclasses.
|
28
|
+
*
|
29
|
+
* @param {Model} _data - The model data to save.
|
30
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
31
|
+
* @returns {Promise<void>}
|
32
|
+
* @abstract
|
33
|
+
*/
|
34
|
+
static putModel(_data) {
|
35
|
+
return Promise.reject(new NotImplementedError(`${this.name} must implement .putModel()`));
|
17
36
|
}
|
18
37
|
|
19
|
-
|
20
|
-
|
38
|
+
/**
|
39
|
+
* Retrieves the index for a given model. This method must be implemented by subclasses.
|
40
|
+
*
|
41
|
+
* @param {Model.constructor} _model - The model to retrieve the index for.
|
42
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
43
|
+
* @returns {Promise<Object>} - Returns a promise resolving to the model index.
|
44
|
+
* @abstract
|
45
|
+
*/
|
46
|
+
static getIndex(_model) {
|
47
|
+
return Promise.reject(new NotImplementedError(`${this.name} does not implement .getIndex()`));
|
21
48
|
}
|
22
49
|
|
23
|
-
|
24
|
-
|
50
|
+
/**
|
51
|
+
* Saves the index for a given model. This method must be implemented by subclasses.
|
52
|
+
*
|
53
|
+
* @param {Object} _index - The index data to save.
|
54
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
55
|
+
* @returns {Promise<void>}
|
56
|
+
* @abstract
|
57
|
+
*/
|
58
|
+
static putIndex(_index) {
|
59
|
+
return Promise.reject(new NotImplementedError(`${this.name} does not implement .putIndex()`));
|
25
60
|
}
|
26
61
|
|
27
|
-
|
28
|
-
|
62
|
+
/**
|
63
|
+
* Retrieves the compiled search index for a model. This method must be implemented by subclasses.
|
64
|
+
*
|
65
|
+
* @param {Model.constructor} _model - The model to retrieve the compiled search index for.
|
66
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
67
|
+
* @returns {Promise<Object>} - Returns a promise resolving to the compiled search index.
|
68
|
+
* @abstract
|
69
|
+
*/
|
70
|
+
static getSearchIndexCompiled(_model) {
|
71
|
+
return Promise.reject(new NotImplementedError(`${this.name} does not implement .getSearchIndexCompiled()`));
|
29
72
|
}
|
30
73
|
|
31
|
-
|
32
|
-
|
74
|
+
/**
|
75
|
+
* Retrieves the raw search index for a model. This method must be implemented by subclasses.
|
76
|
+
*
|
77
|
+
* @param {Model.constructor} _model - The model to retrieve the raw search index for.
|
78
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
79
|
+
* @returns {Promise<Object>} - Returns a promise resolving to the raw search index.
|
80
|
+
* @abstract
|
81
|
+
*/
|
82
|
+
static getSearchIndexRaw(_model) {
|
83
|
+
return Promise.reject(new NotImplementedError(`${this.name} does not implement .getSearchIndexRaw()`));
|
33
84
|
}
|
34
85
|
|
35
|
-
|
36
|
-
|
86
|
+
/**
|
87
|
+
* Saves the compiled search index for a model. This method must be implemented by subclasses.
|
88
|
+
*
|
89
|
+
* @param {Model.constructor} _model - The model for which the compiled search index is saved.
|
90
|
+
* @param {Object} _compiledIndex - The compiled search index data.
|
91
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
92
|
+
* @returns {Promise<void>}
|
93
|
+
* @abstract
|
94
|
+
*/
|
95
|
+
static putSearchIndexCompiled(_model, _compiledIndex) {
|
96
|
+
return Promise.reject(new NotImplementedError(`${this.name} does not implement .putSearchIndexCompiled()`));
|
37
97
|
}
|
38
98
|
|
39
|
-
|
40
|
-
|
99
|
+
/**
|
100
|
+
* Saves the raw search index for a model. This method must be implemented by subclasses.
|
101
|
+
*
|
102
|
+
* @param {Model.constructor} _model - The model for which the raw search index is saved.
|
103
|
+
* @param {Object} _rawIndex - The raw search index data.
|
104
|
+
* @throws {NotImplementedError} Throws if the method is not implemented.
|
105
|
+
* @returns {Promise<void>}
|
106
|
+
* @abstract
|
107
|
+
*/
|
108
|
+
static putSearchIndexRaw(_model, _rawIndex) {
|
109
|
+
return Promise.reject(new NotImplementedError(`${this.name} does not implement .putSearchIndexRaw()`));
|
41
110
|
}
|
42
111
|
|
112
|
+
/**
|
113
|
+
* Performs a search query on a model's index and returns the matching models.
|
114
|
+
*
|
115
|
+
* @param {Model.constructor} model - The model class.
|
116
|
+
* @param {object} query - The search query string.
|
117
|
+
* @returns {Array<Model>} An array of models matching the search query.
|
118
|
+
* @throws {EngineError} Throws if the search index is not available for the model.
|
119
|
+
*/
|
43
120
|
static async search(model, query) {
|
44
121
|
this.checkConfiguration();
|
45
122
|
|
@@ -55,6 +132,7 @@ export default class Engine {
|
|
55
132
|
for (const result of results) {
|
56
133
|
output.push({
|
57
134
|
...result,
|
135
|
+
score: Number(result.score.toFixed(4)),
|
58
136
|
model: await this.get(model, result.ref),
|
59
137
|
});
|
60
138
|
}
|
@@ -62,6 +140,13 @@ export default class Engine {
|
|
62
140
|
return output;
|
63
141
|
}
|
64
142
|
|
143
|
+
/**
|
144
|
+
* Finds models that match a query in the model's index.
|
145
|
+
*
|
146
|
+
* @param {Model.constructor} model - The model class to search.
|
147
|
+
* @param {object} query - The query object containing search criteria.
|
148
|
+
* @returns {Array<Model>} An array of models matching the query.
|
149
|
+
*/
|
65
150
|
static async find(model, query) {
|
66
151
|
this.checkConfiguration();
|
67
152
|
const index = await this.getIndex(model);
|
@@ -69,50 +154,57 @@ export default class Engine {
|
|
69
154
|
return new Query(query).execute(model, index);
|
70
155
|
}
|
71
156
|
|
157
|
+
/**
|
158
|
+
* Stores a model and its associated index data into the system.
|
159
|
+
*
|
160
|
+
* @param {Model} model - The model to store.
|
161
|
+
* @throws {EngineError} Throws if the model fails to validate or index fails to save.
|
162
|
+
*/
|
72
163
|
static async put(model) {
|
73
164
|
this.checkConfiguration();
|
74
165
|
const uploadedModels = [];
|
75
166
|
const indexUpdates = {};
|
76
167
|
|
77
|
-
const processModel = async (
|
78
|
-
if (uploadedModels.includes(
|
79
|
-
|
168
|
+
const processModel = async (m) => {
|
169
|
+
if (!uploadedModels.includes(m.id)) {
|
170
|
+
m.validate();
|
80
171
|
|
81
|
-
|
172
|
+
await this.putModel(m);
|
82
173
|
|
83
|
-
|
84
|
-
|
174
|
+
uploadedModels.push(m.id);
|
175
|
+
indexUpdates[m.constructor.name] = (indexUpdates[m.constructor.name] ?? []).concat([m]);
|
85
176
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
177
|
+
if (m.constructor.searchProperties().length > 0) {
|
178
|
+
const rawSearchIndex = {
|
179
|
+
...await this.getSearchIndexRaw(m.constructor),
|
180
|
+
[m.id]: m.toSearchData(),
|
181
|
+
};
|
91
182
|
|
92
|
-
|
183
|
+
await this.putSearchIndexRaw(m.constructor, rawSearchIndex);
|
93
184
|
|
94
|
-
|
95
|
-
|
185
|
+
const compiledIndex = lunr(function () {
|
186
|
+
this.ref('id');
|
96
187
|
|
97
|
-
|
98
|
-
|
99
|
-
|
188
|
+
for (const field of m.constructor.searchProperties()) {
|
189
|
+
this.field(field);
|
190
|
+
}
|
100
191
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
192
|
+
Object.values(rawSearchIndex).forEach(function (doc) {
|
193
|
+
this.add(doc);
|
194
|
+
}, this);
|
195
|
+
});
|
105
196
|
|
106
|
-
|
107
|
-
}
|
108
|
-
|
109
|
-
for (const [_, property] of Object.entries(model)) {
|
110
|
-
if (Type.Model.isModel(property)) {
|
111
|
-
await processModel(property);
|
197
|
+
await this.putSearchIndexCompiled(m.constructor, compiledIndex);
|
112
198
|
}
|
113
|
-
|
114
|
-
|
115
|
-
|
199
|
+
|
200
|
+
for (const [_, property] of Object.entries(m)) {
|
201
|
+
if (Type.Model.isModel(property)) {
|
202
|
+
await processModel(property);
|
203
|
+
}
|
204
|
+
if (Array.isArray(property) && Type.Model.isModel(property[0])) {
|
205
|
+
for (const subModel of property) {
|
206
|
+
await processModel(subModel);
|
207
|
+
}
|
116
208
|
}
|
117
209
|
}
|
118
210
|
}
|
@@ -122,6 +214,14 @@ export default class Engine {
|
|
122
214
|
await this.putIndex(indexUpdates);
|
123
215
|
}
|
124
216
|
|
217
|
+
/**
|
218
|
+
* Retrieves a model by its ID and converts it to its data representation.
|
219
|
+
*
|
220
|
+
* @param {Model.constructor} model - The model class to retrieve.
|
221
|
+
* @param {string} id - The ID of the model to retrieve.
|
222
|
+
* @returns {Model} The found model.
|
223
|
+
* @throws {NotFoundEngineError} Throws if the model is not found.
|
224
|
+
*/
|
125
225
|
static async get(model, id) {
|
126
226
|
this.checkConfiguration();
|
127
227
|
|
@@ -134,6 +234,12 @@ export default class Engine {
|
|
134
234
|
}
|
135
235
|
}
|
136
236
|
|
237
|
+
/**
|
238
|
+
* Hydrates a model by populating its related properties (e.g., submodels) from stored data.
|
239
|
+
*
|
240
|
+
* @param {Model} model - The model to hydrate.
|
241
|
+
* @returns {Model} The hydrated model.
|
242
|
+
*/
|
137
243
|
static async hydrate(model) {
|
138
244
|
this.checkConfiguration();
|
139
245
|
const hydratedModels = {};
|
@@ -189,53 +295,122 @@ export default class Engine {
|
|
189
295
|
|
190
296
|
function getSubModelClass(modelToProcess, name, isArray = false) {
|
191
297
|
const constructorField = modelToProcess.constructor[name];
|
298
|
+
|
192
299
|
if (constructorField instanceof Function && !constructorField.prototype) {
|
193
300
|
return isArray ? constructorField()._items : constructorField();
|
194
301
|
}
|
302
|
+
|
195
303
|
return isArray ? constructorField._items : constructorField;
|
196
304
|
}
|
197
305
|
|
198
306
|
return await hydrateModel(await this.get(model.constructor, model.id));
|
199
307
|
}
|
200
308
|
|
309
|
+
/**
|
310
|
+
* Configures the engine with specific settings.
|
311
|
+
*
|
312
|
+
* @param {Object} configuration - The configuration settings for the engine.
|
313
|
+
* @returns {Engine} A new engine instance with the applied configuration.
|
314
|
+
*/
|
201
315
|
static configure(configuration) {
|
202
316
|
class ConfiguredStore extends this {
|
203
|
-
static
|
317
|
+
static configuration = configuration;
|
204
318
|
}
|
205
319
|
|
206
|
-
Object.defineProperty(ConfiguredStore, 'name', {value: `${this.toString()}`});
|
320
|
+
Object.defineProperty(ConfiguredStore, 'name', { value: `${this.toString()}` });
|
207
321
|
|
208
322
|
return ConfiguredStore;
|
209
323
|
}
|
210
324
|
|
325
|
+
/**
|
326
|
+
* Checks if the engine is properly configured.
|
327
|
+
*
|
328
|
+
* @throws {MissConfiguredError} Throws if the engine is misconfigured.
|
329
|
+
* @abstract
|
330
|
+
*/
|
211
331
|
static checkConfiguration() {
|
212
|
-
|
332
|
+
// Implemented in extending Engine class
|
213
333
|
}
|
214
334
|
|
335
|
+
/**
|
336
|
+
* Returns the name of the engine class.
|
337
|
+
*
|
338
|
+
* @returns {string} The name of the engine class.
|
339
|
+
*/
|
215
340
|
static toString() {
|
216
341
|
return this.name;
|
217
342
|
}
|
218
|
-
}
|
343
|
+
}
|
219
344
|
|
345
|
+
/**
|
346
|
+
* Represents a general error that occurs within the engine.
|
347
|
+
* Extends the built-in `Error` class.
|
348
|
+
*/
|
220
349
|
export class EngineError extends Error {
|
350
|
+
/**
|
351
|
+
* The underlying error that caused this engine error, if available.
|
352
|
+
* @type {Error|undefined}
|
353
|
+
*/
|
221
354
|
underlyingError;
|
355
|
+
|
356
|
+
/**
|
357
|
+
* Creates an instance of `EngineError`.
|
358
|
+
*
|
359
|
+
* @param {string} message - The error message.
|
360
|
+
* @param {Error} [error] - An optional underlying error that caused this error.
|
361
|
+
*/
|
222
362
|
constructor(message, error = undefined) {
|
223
363
|
super(message);
|
224
364
|
this.underlyingError = error;
|
225
365
|
}
|
226
366
|
}
|
227
367
|
|
368
|
+
/**
|
369
|
+
* Represents an error that occurs when a requested resource or item is not found in the engine.
|
370
|
+
* Extends the `EngineError` class.
|
371
|
+
*/
|
228
372
|
export class NotFoundEngineError extends EngineError {
|
373
|
+
/**
|
374
|
+
* Creates an instance of `NotFoundEngineError`.
|
375
|
+
*
|
376
|
+
* @param {string} message - The error message.
|
377
|
+
* @param {Error} [error] - An optional underlying error that caused this error.
|
378
|
+
*/
|
229
379
|
}
|
230
380
|
|
381
|
+
/**
|
382
|
+
* Represents an error indicating that a certain method or functionality is not implemented in the engine.
|
383
|
+
* Extends the `EngineError` class.
|
384
|
+
*/
|
231
385
|
export class NotImplementedError extends EngineError {
|
386
|
+
/**
|
387
|
+
* Creates an instance of `NotImplementedError`.
|
388
|
+
*
|
389
|
+
* @param {string} message - The error message.
|
390
|
+
* @param {Error} [error] - An optional underlying error that caused this error.
|
391
|
+
*/
|
232
392
|
}
|
233
393
|
|
394
|
+
/**
|
395
|
+
* Represents an error indicating that the engine is misconfigured.
|
396
|
+
* Extends the `EngineError` class.
|
397
|
+
*/
|
234
398
|
export class MissConfiguredError extends EngineError {
|
399
|
+
/**
|
400
|
+
* The configuration that led to the misconfiguration error.
|
401
|
+
* @type {Object}
|
402
|
+
*/
|
235
403
|
configuration;
|
236
404
|
|
405
|
+
/**
|
406
|
+
* Creates an instance of `MissConfiguredError`.
|
407
|
+
*
|
408
|
+
* @param {Object} configuration - The configuration object that caused the misconfiguration.
|
409
|
+
*/
|
237
410
|
constructor(configuration) {
|
238
411
|
super('Engine is miss-configured');
|
239
412
|
this.configuration = configuration;
|
240
413
|
}
|
241
414
|
}
|
415
|
+
|
416
|
+
export default Engine;
|
package/src/engine/FileEngine.js
CHANGED
@@ -1,16 +1,36 @@
|
|
1
|
-
import Engine, {EngineError, MissConfiguredError} from './Engine.js';
|
2
|
-
import {dirname, join} from 'node:path';
|
1
|
+
import Engine, { EngineError, MissConfiguredError } from './Engine.js';
|
2
|
+
import { dirname, join } from 'node:path';
|
3
3
|
import fs from 'node:fs/promises';
|
4
4
|
|
5
|
+
/**
|
6
|
+
* Custom error class for FileEngine-related errors.
|
7
|
+
* Extends the base `EngineError` class.
|
8
|
+
*/
|
5
9
|
class FileEngineError extends EngineError {}
|
6
10
|
|
11
|
+
/**
|
12
|
+
* Error thrown when writing to a file fails in `FileEngine`.
|
13
|
+
* Extends the `FileEngineError` class.
|
14
|
+
*/
|
7
15
|
class FailedWriteFileEngineError extends FileEngineError {}
|
8
16
|
|
9
17
|
/**
|
18
|
+
* `FileEngine` class extends the base `Engine` class to implement
|
19
|
+
* file system-based storage and retrieval of model data.
|
20
|
+
*
|
10
21
|
* @class FileEngine
|
11
22
|
* @extends Engine
|
12
23
|
*/
|
13
|
-
|
24
|
+
class FileEngine extends Engine {
|
25
|
+
/**
|
26
|
+
* Configures the FileEngine with a given configuration object.
|
27
|
+
* Adds default `filesystem` configuration if not provided.
|
28
|
+
*
|
29
|
+
* @param {Object} configuration - Configuration settings for FileEngine.
|
30
|
+
* @param {Object} [configuration.filesystem] - Custom filesystem module (default: Node.js fs/promises).
|
31
|
+
* @param {Object} [configuration.path] - The absolute path on the filesystem to write models to.
|
32
|
+
* @returns {FileEngine} A configured instance of FileEngine.
|
33
|
+
*/
|
14
34
|
static configure(configuration) {
|
15
35
|
if (!configuration.filesystem) {
|
16
36
|
configuration.filesystem = fs;
|
@@ -18,42 +38,77 @@ export default class FileEngine extends Engine {
|
|
18
38
|
return super.configure(configuration);
|
19
39
|
}
|
20
40
|
|
41
|
+
/**
|
42
|
+
* Checks if the FileEngine has been configured correctly.
|
43
|
+
* Ensures that `path` and `filesystem` settings are present.
|
44
|
+
*
|
45
|
+
* @throws {MissConfiguredError} Throws if required configuration is missing.
|
46
|
+
*/
|
21
47
|
static checkConfiguration() {
|
22
|
-
if (
|
23
|
-
|
24
|
-
|
25
|
-
) throw new MissConfiguredError(this._configuration);
|
48
|
+
if (!this.configuration?.path || !this.configuration?.filesystem) {
|
49
|
+
throw new MissConfiguredError(this.configuration);
|
50
|
+
}
|
26
51
|
}
|
27
52
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
53
|
+
/**
|
54
|
+
* Retrieves a model by its ID from the file system.
|
55
|
+
*
|
56
|
+
* @param {string} id - The ID of the model to retrieve.
|
57
|
+
* @returns {Promise<Object>} The parsed model data.
|
58
|
+
* @throws {Error} Throws if the file cannot be read or parsed.
|
59
|
+
*/
|
60
|
+
static getById(id) {
|
61
|
+
return this.configuration.filesystem
|
62
|
+
.readFile(join(this.configuration.path, `${id}.json`))
|
63
|
+
.then((b) => b.toString())
|
64
|
+
.then(JSON.parse);
|
32
65
|
}
|
33
66
|
|
34
|
-
|
35
|
-
|
67
|
+
/**
|
68
|
+
* Retrieves the index for a given model from the file system.
|
69
|
+
*
|
70
|
+
* @param {Model.constructor?} model - The model for which the index is retrieved.
|
71
|
+
* @returns {Promise<Object>} The parsed index data.
|
72
|
+
* @throws {Error} Throws if the file cannot be read.
|
73
|
+
*/
|
74
|
+
static getIndex(model) {
|
75
|
+
return this.configuration.filesystem
|
76
|
+
.readFile(join(this.configuration.path, model.toString(), '_index.json'))
|
77
|
+
.then((b) => b.toString())
|
78
|
+
.catch(() => '{}')
|
79
|
+
.then(JSON.parse);
|
36
80
|
}
|
37
81
|
|
82
|
+
/**
|
83
|
+
* Saves a model to the file system.
|
84
|
+
*
|
85
|
+
* @param {Model} model - The model to save.
|
86
|
+
* @throws {FailedWriteFileEngineError} Throws if the model cannot be written to the file system.
|
87
|
+
*/
|
38
88
|
static async putModel(model) {
|
39
|
-
const filePath = join(this.
|
40
|
-
|
89
|
+
const filePath = join(this.configuration.path, `${model.id}.json`);
|
41
90
|
try {
|
42
|
-
await this.
|
43
|
-
await this.
|
91
|
+
await this.configuration.filesystem.mkdir(dirname(filePath), { recursive: true });
|
92
|
+
await this.configuration.filesystem.writeFile(filePath, JSON.stringify(model.toData()));
|
44
93
|
} catch (error) {
|
45
94
|
throw new FailedWriteFileEngineError(`Failed to put file://${filePath}`, error);
|
46
95
|
}
|
47
96
|
}
|
48
97
|
|
98
|
+
/**
|
99
|
+
* Saves the index for multiple models to the file system.
|
100
|
+
*
|
101
|
+
* @param {Object} index - An object where keys are locations and values are arrays of models.
|
102
|
+
* @throws {FailedWriteFileEngineError} Throws if the index cannot be written to the file system.
|
103
|
+
*/
|
49
104
|
static async putIndex(index) {
|
50
105
|
const processIndex = async (location, models) => {
|
51
|
-
const modelIndex = Object.fromEntries(models.map(m => [m.id, m.toIndexData()]));
|
52
|
-
const filePath = join(this.
|
53
|
-
const currentIndex = JSON.parse((await this.
|
106
|
+
const modelIndex = Object.fromEntries(models.map((m) => [m.id, m.toIndexData()]));
|
107
|
+
const filePath = join(this.configuration.path, location, '_index.json');
|
108
|
+
const currentIndex = JSON.parse((await this.configuration.filesystem.readFile(filePath).catch(() => '{}')).toString());
|
54
109
|
|
55
110
|
try {
|
56
|
-
await this.
|
111
|
+
await this.configuration.filesystem.writeFile(filePath, JSON.stringify({
|
57
112
|
...currentIndex,
|
58
113
|
...modelIndex,
|
59
114
|
}));
|
@@ -69,35 +124,66 @@ export default class FileEngine extends Engine {
|
|
69
124
|
await processIndex('', Object.values(index).flat());
|
70
125
|
}
|
71
126
|
|
72
|
-
|
73
|
-
|
74
|
-
|
127
|
+
/**
|
128
|
+
* Retrieves the compiled search index for a model from the file system.
|
129
|
+
*
|
130
|
+
* @param {Model.constructor} model - The model for which the search index is retrieved.
|
131
|
+
* @returns {Promise<Object>} The parsed compiled search index.
|
132
|
+
* @throws {Error} Throws if the file cannot be read.
|
133
|
+
*/
|
134
|
+
static getSearchIndexCompiled(model) {
|
135
|
+
return this.configuration.filesystem
|
136
|
+
.readFile(join(this.configuration.path, model.toString(), '_search_index.json'))
|
137
|
+
.then((b) => b.toString())
|
75
138
|
.then(JSON.parse);
|
76
139
|
}
|
77
140
|
|
78
|
-
|
79
|
-
|
80
|
-
|
141
|
+
/**
|
142
|
+
* Retrieves the raw search index for a model from the file system.
|
143
|
+
*
|
144
|
+
* @param {Model.constructor} model - The model for which the raw search index is retrieved.
|
145
|
+
* @returns {Promise<Object>} The parsed raw search index.
|
146
|
+
* @throws {Error} Throws if the file cannot be read.
|
147
|
+
*/
|
148
|
+
static getSearchIndexRaw(model) {
|
149
|
+
return this.configuration.filesystem
|
150
|
+
.readFile(join(this.configuration.path, model.toString(), '_search_index_raw.json'))
|
151
|
+
.then((b) => b.toString())
|
81
152
|
.then(JSON.parse)
|
82
153
|
.catch(() => ({}));
|
83
154
|
}
|
84
155
|
|
156
|
+
/**
|
157
|
+
* Saves the compiled search index for a model to the file system.
|
158
|
+
*
|
159
|
+
* @param {Model.constructor} model - The model for which the compiled search index is saved.
|
160
|
+
* @param {Object} compiledIndex - The compiled search index to save.
|
161
|
+
* @throws {FailedWriteFileEngineError} Throws if the compiled index cannot be written to the file system.
|
162
|
+
*/
|
85
163
|
static async putSearchIndexCompiled(model, compiledIndex) {
|
86
|
-
const filePath = join(this.
|
87
|
-
|
164
|
+
const filePath = join(this.configuration.path, model.toString(), '_search_index.json');
|
88
165
|
try {
|
89
|
-
await this.
|
166
|
+
await this.configuration.filesystem.writeFile(filePath, JSON.stringify(compiledIndex));
|
90
167
|
} catch (error) {
|
91
168
|
throw new FailedWriteFileEngineError(`Failed to put file://${filePath}`, error);
|
92
169
|
}
|
93
170
|
}
|
94
171
|
|
172
|
+
/**
|
173
|
+
* Saves the raw search index for a model to the file system.
|
174
|
+
*
|
175
|
+
* @param {Model.constructor} model - The model for which the raw search index is saved.
|
176
|
+
* @param {Object} rawIndex - The raw search index to save.
|
177
|
+
* @throws {FailedWriteFileEngineError} Throws if the raw index cannot be written to the file system.
|
178
|
+
*/
|
95
179
|
static async putSearchIndexRaw(model, rawIndex) {
|
96
|
-
const filePath = join(this.
|
180
|
+
const filePath = join(this.configuration.path, model.toString(), '_search_index_raw.json');
|
97
181
|
try {
|
98
|
-
await this.
|
182
|
+
await this.configuration.filesystem.writeFile(filePath, JSON.stringify(rawIndex));
|
99
183
|
} catch (error) {
|
100
184
|
throw new FailedWriteFileEngineError(`Failed to put file://${filePath}`, error);
|
101
185
|
}
|
102
186
|
}
|
103
187
|
}
|
188
|
+
|
189
|
+
export default FileEngine;
|