@airoom/nextmin-node 1.4.6 → 2.0.1
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/README.md +48 -5
- package/dist/api/apiRouter.d.ts +2 -0
- package/dist/api/apiRouter.js +67 -21
- package/dist/api/router/mountCrudRoutes.js +207 -220
- package/dist/api/router/mountFindRoutes.js +2 -49
- package/dist/api/router/mountSearchRoutes.js +10 -52
- package/dist/api/router/mountSearchRoutes_extended.js +7 -48
- package/dist/api/router/utils.js +20 -7
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +83 -0
- package/dist/database/DatabaseAdapter.d.ts +7 -0
- package/dist/database/NMAdapter.d.ts +41 -0
- package/dist/database/NMAdapter.js +979 -0
- package/dist/database/QueryEngine.d.ts +14 -0
- package/dist/database/QueryEngine.js +215 -0
- package/dist/database/utils.d.ts +2 -0
- package/dist/database/utils.js +21 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +11 -5
- package/dist/models/BaseModel.d.ts +16 -0
- package/dist/models/BaseModel.js +32 -4
- package/dist/policy/authorize.js +95 -38
- package/dist/schemas/Users.json +66 -30
- package/dist/services/RealtimeService.d.ts +20 -0
- package/dist/services/RealtimeService.js +93 -0
- package/dist/services/SchemaService.d.ts +3 -0
- package/dist/services/SchemaService.js +6 -2
- package/dist/utils/DefaultDataInitializer.js +10 -2
- package/dist/utils/Events.d.ts +34 -0
- package/dist/utils/Events.js +55 -0
- package/dist/utils/Logger.js +12 -10
- package/dist/utils/QueryCache.d.ts +16 -0
- package/dist/utils/QueryCache.js +106 -0
- package/dist/utils/SchemaLoader.d.ts +5 -0
- package/dist/utils/SchemaLoader.js +45 -3
- package/package.json +19 -4
- package/dist/database/InMemoryAdapter.d.ts +0 -15
- package/dist/database/InMemoryAdapter.js +0 -71
- package/dist/database/MongoAdapter.d.ts +0 -52
- package/dist/database/MongoAdapter.js +0 -410
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.MongoAdapter = void 0;
|
|
40
|
-
const mongoose_1 = __importStar(require("mongoose"));
|
|
41
|
-
const mongoose_autopopulate_1 = __importDefault(require("mongoose-autopopulate"));
|
|
42
|
-
const Logger_1 = __importDefault(require("../utils/Logger"));
|
|
43
|
-
const SchemaLoader_1 = require("../utils/SchemaLoader");
|
|
44
|
-
// Prevent Mongoose from pluralizing model names automatically
|
|
45
|
-
mongoose_1.default.pluralize(null);
|
|
46
|
-
class MongoAdapter {
|
|
47
|
-
constructor(url, dbName) {
|
|
48
|
-
this.url = url;
|
|
49
|
-
this.dbName = dbName;
|
|
50
|
-
this.connection = mongoose_1.default.createConnection(`${this.url}/${this.dbName}`);
|
|
51
|
-
this.models = new Map();
|
|
52
|
-
}
|
|
53
|
-
async connect() {
|
|
54
|
-
await this.connection.asPromise();
|
|
55
|
-
Logger_1.default.info('MongoAdapter', `Connected to MongoDB database: ${this.dbName}`);
|
|
56
|
-
}
|
|
57
|
-
async disconnect() {
|
|
58
|
-
await this.connection.close();
|
|
59
|
-
Logger_1.default.warn('MongoAdapter', 'Disconnected from MongoDB database');
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Register (and re-register) all schemas.
|
|
63
|
-
* - Drops compiled models that no longer exist.
|
|
64
|
-
* - Recompiles every current model so validators reflect latest schema.
|
|
65
|
-
*/
|
|
66
|
-
async registerSchemas(schemas) {
|
|
67
|
-
// Build the incoming set from schema.modelName (not object keys!)
|
|
68
|
-
const incoming = new Set(Object.values(schemas).map((s) => s.modelName.toLowerCase()));
|
|
69
|
-
// 1) Remove compiled models that are no longer present
|
|
70
|
-
for (const name of Array.from(this.models.keys())) {
|
|
71
|
-
if (!incoming.has(name)) {
|
|
72
|
-
this.safeDeleteModel(name);
|
|
73
|
-
this.models.delete(name);
|
|
74
|
-
// Optional: also drop the collection (disabled by default)
|
|
75
|
-
// await this.safeDropCollection(name);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// 2) (Re)compile every current model so validators and fields are fresh
|
|
79
|
-
for (const def of Object.values(schemas)) {
|
|
80
|
-
const modelKey = def.modelName; // e.g. "Users"
|
|
81
|
-
const modelKeyLc = modelKey.toLowerCase();
|
|
82
|
-
const collection = def.collection?.toLowerCase() ?? modelKeyLc; // e.g. "users"
|
|
83
|
-
// always delete previous compiled model to avoid OverwriteModelError and stale validators
|
|
84
|
-
this.safeDeleteModel(modelKey);
|
|
85
|
-
const mSchema = this.buildMongooseSchema(def);
|
|
86
|
-
const model = this.connection.model(modelKey, mSchema, collection);
|
|
87
|
-
this.models.set(modelKeyLc, model);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/** Optional hook used by the router when schemas are removed. */
|
|
91
|
-
async unregisterSchemas(names) {
|
|
92
|
-
for (const raw of names) {
|
|
93
|
-
const lc = raw.toLowerCase();
|
|
94
|
-
this.safeDeleteModel(lc);
|
|
95
|
-
this.models.delete(lc);
|
|
96
|
-
// Optional: await this.safeDropCollection(lc);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/** Optional single-model drop hook. */
|
|
100
|
-
async dropModel(nameRaw) {
|
|
101
|
-
const lc = nameRaw.toLowerCase();
|
|
102
|
-
this.safeDeleteModel(lc);
|
|
103
|
-
this.models.delete(lc);
|
|
104
|
-
// Optional: await this.safeDropCollection(lc);
|
|
105
|
-
}
|
|
106
|
-
// ---------------- Internals ----------------
|
|
107
|
-
/**
|
|
108
|
-
* Delete a compiled model by case-insensitive match.
|
|
109
|
-
* Works whether we compiled it as "Users" or "users".
|
|
110
|
-
*/
|
|
111
|
-
safeDeleteModel(nameOrLc) {
|
|
112
|
-
try {
|
|
113
|
-
const targetLc = nameOrLc.toLowerCase();
|
|
114
|
-
const keys = Object.keys(this.connection.models);
|
|
115
|
-
for (const k of keys) {
|
|
116
|
-
if (k.toLowerCase() === targetLc) {
|
|
117
|
-
this.connection.deleteModel(k);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
/* ignore */
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
async safeDropCollection(name) {
|
|
126
|
-
try {
|
|
127
|
-
await this.connection.dropCollection(name);
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
130
|
-
// ignore "ns not found" etc.
|
|
131
|
-
if (e?.codeName !== 'NamespaceNotFound') {
|
|
132
|
-
Logger_1.default.warn('MongoAdapter', `dropCollection(${name}) warning:`, e?.message || e);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/** Build a fresh Mongoose schema from our NextMin schema definition. */
|
|
137
|
-
buildMongooseSchema(def) {
|
|
138
|
-
const shape = {};
|
|
139
|
-
// If this schema extends a base, only include child-own attributes for storage
|
|
140
|
-
let baseKeys = null;
|
|
141
|
-
const baseName = def?.extends;
|
|
142
|
-
if (baseName) {
|
|
143
|
-
const baseSchema = this.getSchema(baseName);
|
|
144
|
-
if (baseSchema) {
|
|
145
|
-
baseKeys = new Set(Object.keys(baseSchema.attributes || {}));
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
for (const [key, attr] of Object.entries(def.attributes)) {
|
|
149
|
-
// Exclude base attributes from child storage schema; always allow link field
|
|
150
|
-
if (baseKeys && key !== 'baseId' && baseKeys.has(key))
|
|
151
|
-
continue;
|
|
152
|
-
shape[key] = this.mapAttribute(attr);
|
|
153
|
-
}
|
|
154
|
-
const s = new mongoose_1.Schema(shape, { timestamps: true });
|
|
155
|
-
// Hide private fields by default when toObject() is used (unless overridden)
|
|
156
|
-
s.set('toObject', {
|
|
157
|
-
transform: (_doc, ret) => {
|
|
158
|
-
for (const [key, attr] of Object.entries(def.attributes)) {
|
|
159
|
-
if (!Array.isArray(attr) && attr.private) {
|
|
160
|
-
delete ret[key];
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return ret;
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
s.plugin(mongoose_autopopulate_1.default);
|
|
167
|
-
return s;
|
|
168
|
-
}
|
|
169
|
-
mapAttribute(attribute) {
|
|
170
|
-
// Arrays
|
|
171
|
-
if (Array.isArray(attribute)) {
|
|
172
|
-
const elem = attribute[0] || { type: 'string' };
|
|
173
|
-
const mapped = this.mapScalar(elem);
|
|
174
|
-
// Mongoose "array of refs" works best as [{ type: X, ref: 'Y', autopopulate: true }]
|
|
175
|
-
const arrItem = { ...mapped };
|
|
176
|
-
// required belongs on the path, not the inner type
|
|
177
|
-
const required = !!elem.required;
|
|
178
|
-
delete arrItem.required;
|
|
179
|
-
return { type: [arrItem], required };
|
|
180
|
-
}
|
|
181
|
-
// Single
|
|
182
|
-
return this.mapScalar(attribute);
|
|
183
|
-
}
|
|
184
|
-
mapScalar(attr) {
|
|
185
|
-
const typeMapping = {
|
|
186
|
-
string: String,
|
|
187
|
-
number: Number,
|
|
188
|
-
boolean: Boolean,
|
|
189
|
-
date: Date,
|
|
190
|
-
array: Array,
|
|
191
|
-
object: Object,
|
|
192
|
-
objectid: mongoose_1.default.Schema.Types.ObjectId,
|
|
193
|
-
};
|
|
194
|
-
const out = {
|
|
195
|
-
type: typeMapping[String(attr?.type || 'string').toLowerCase()] || String,
|
|
196
|
-
};
|
|
197
|
-
// Only set when truthy to avoid forcing behavior when unset
|
|
198
|
-
if (attr?.required === true)
|
|
199
|
-
out.required = true;
|
|
200
|
-
if (attr?.unique === true)
|
|
201
|
-
out.unique = true;
|
|
202
|
-
if (attr?.ref) {
|
|
203
|
-
out.ref = attr.ref;
|
|
204
|
-
out.autopopulate = true;
|
|
205
|
-
}
|
|
206
|
-
if (attr?.default !== undefined) {
|
|
207
|
-
if (String(attr.type).toLowerCase() === 'date' &&
|
|
208
|
-
attr.default === 'now') {
|
|
209
|
-
out.default = Date.now;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
out.default = attr.default;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// Optional common validators
|
|
216
|
-
if (attr?.enum)
|
|
217
|
-
out.enum = attr.enum;
|
|
218
|
-
if (attr?.min !== undefined)
|
|
219
|
-
out.min = attr.min;
|
|
220
|
-
if (attr?.max !== undefined)
|
|
221
|
-
out.max = attr.max;
|
|
222
|
-
if (attr?.minLength !== undefined)
|
|
223
|
-
out.minLength = attr.minLength;
|
|
224
|
-
if (attr?.maxLength !== undefined)
|
|
225
|
-
out.maxLength = attr.maxLength;
|
|
226
|
-
if (attr?.match)
|
|
227
|
-
out.match = new RegExp(attr.match);
|
|
228
|
-
return out;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Resolve (or lazily create) the compiled model for a schema.
|
|
232
|
-
* Uses schema.modelName as the Mongoose model name and
|
|
233
|
-
* (schema.collection ?? modelName).toLowerCase() as the collection.
|
|
234
|
-
*/
|
|
235
|
-
getModel(collection, schemaDefinition) {
|
|
236
|
-
const modelKey = schemaDefinition.modelName; // e.g. "Users"
|
|
237
|
-
const modelKeyLc = modelKey.toLowerCase();
|
|
238
|
-
const collectionName = schemaDefinition.collection?.toLowerCase() ??
|
|
239
|
-
collection.toLowerCase();
|
|
240
|
-
// Prefer cached by lowercased model key
|
|
241
|
-
const cached = this.models.get(modelKeyLc);
|
|
242
|
-
if (cached)
|
|
243
|
-
return cached;
|
|
244
|
-
// Ensure any dependency (rare) – preserved from your original example
|
|
245
|
-
if (schemaDefinition.attributes.author?.ref === 'Users') {
|
|
246
|
-
const userSchema = this.getSchema('Users');
|
|
247
|
-
if (!userSchema)
|
|
248
|
-
throw new Error(`Missing schema for Users`);
|
|
249
|
-
if (!this.models.has('users')) {
|
|
250
|
-
const uModel = this.connection.model(userSchema.modelName, this.buildMongooseSchema(userSchema), userSchema.collection?.toLowerCase() ??
|
|
251
|
-
userSchema.modelName.toLowerCase());
|
|
252
|
-
this.models.set('users', uModel);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
// Remove any stale compiled model(s) with same name (case-insensitive), then (re)create
|
|
256
|
-
this.safeDeleteModel(modelKey);
|
|
257
|
-
const model = this.connection.model(modelKey, this.buildMongooseSchema(schemaDefinition), collectionName);
|
|
258
|
-
this.models.set(modelKeyLc, model);
|
|
259
|
-
return model;
|
|
260
|
-
}
|
|
261
|
-
getSchema(modelName) {
|
|
262
|
-
// Use the singleton loader so we read the same live map as the router
|
|
263
|
-
const loader = SchemaLoader_1.SchemaLoader.getInstance?.() ?? new SchemaLoader_1.SchemaLoader();
|
|
264
|
-
const schemas = loader.getSchemas();
|
|
265
|
-
// The loader returns a map keyed by modelName; prefer lookup by value
|
|
266
|
-
const byName = Object.values(schemas).find((s) => String(s.modelName).toLowerCase() === modelName.toLowerCase());
|
|
267
|
-
return byName;
|
|
268
|
-
}
|
|
269
|
-
convertQueryFieldsToObjectId(query, schemaDefinition) {
|
|
270
|
-
const newQuery = { ...query };
|
|
271
|
-
// Remap 'id' to '_id'
|
|
272
|
-
if (newQuery.id !== undefined) {
|
|
273
|
-
try {
|
|
274
|
-
newQuery._id = new mongoose_1.default.Types.ObjectId(String(newQuery.id));
|
|
275
|
-
}
|
|
276
|
-
catch {
|
|
277
|
-
// leave as-is if not a valid ObjectId
|
|
278
|
-
newQuery._id = newQuery.id;
|
|
279
|
-
}
|
|
280
|
-
delete newQuery.id;
|
|
281
|
-
}
|
|
282
|
-
// Convert any fields defined as objectid
|
|
283
|
-
for (const [key, attribute] of Object.entries(schemaDefinition.attributes)) {
|
|
284
|
-
if (!Array.isArray(attribute) &&
|
|
285
|
-
String(attribute.type || '').toLowerCase() === 'objectid') {
|
|
286
|
-
const val = newQuery[key];
|
|
287
|
-
if (val === undefined || val === null)
|
|
288
|
-
continue;
|
|
289
|
-
const toObjectId = (v) => {
|
|
290
|
-
try {
|
|
291
|
-
return new mongoose_1.default.Types.ObjectId(String(v));
|
|
292
|
-
}
|
|
293
|
-
catch {
|
|
294
|
-
Logger_1.default.warn('MongoAdapter', `Invalid ObjectId for '${key}': ${v}`);
|
|
295
|
-
return v;
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
if (Array.isArray(val)) {
|
|
299
|
-
newQuery[key] = val.map(toObjectId);
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
newQuery[key] = toObjectId(val);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
return newQuery;
|
|
307
|
-
}
|
|
308
|
-
toAppObject(doc, includePrivateFields) {
|
|
309
|
-
if (!doc)
|
|
310
|
-
return doc;
|
|
311
|
-
const obj = doc.toObject(includePrivateFields
|
|
312
|
-
? { transform: (_doc, ret) => ret } // override to keep private fields
|
|
313
|
-
: undefined);
|
|
314
|
-
if (obj?._id !== undefined) {
|
|
315
|
-
obj.id = String(obj._id);
|
|
316
|
-
delete obj._id;
|
|
317
|
-
}
|
|
318
|
-
if ('__v' in obj)
|
|
319
|
-
delete obj.__v;
|
|
320
|
-
return obj;
|
|
321
|
-
}
|
|
322
|
-
// ---------------- CRUD ----------------
|
|
323
|
-
async create(collection, data, schemaDefinition, includePrivateFields = false) {
|
|
324
|
-
const model = this.getModel(collection, schemaDefinition);
|
|
325
|
-
const doc = new model(data);
|
|
326
|
-
const saved = await doc.save();
|
|
327
|
-
return this.toAppObject(saved, includePrivateFields);
|
|
328
|
-
}
|
|
329
|
-
async read(collection, query, limit, skip, schemaDefinition, includePrivateFields, options) {
|
|
330
|
-
const model = this.getModel(collection, schemaDefinition);
|
|
331
|
-
const q = this.convertQueryFieldsToObjectId(query, schemaDefinition);
|
|
332
|
-
let cursor = model.find(q, options?.projection); // ✅ projection support (optional)
|
|
333
|
-
if (options?.sort && Object.keys(options.sort).length) {
|
|
334
|
-
cursor = cursor.sort(options.sort); // ✅ apply sort
|
|
335
|
-
}
|
|
336
|
-
if (limit !== undefined && skip !== undefined) {
|
|
337
|
-
cursor = cursor.limit(limit).skip(skip);
|
|
338
|
-
}
|
|
339
|
-
const results = await cursor.exec();
|
|
340
|
-
return results.map((doc) => this.toAppObject(doc, includePrivateFields));
|
|
341
|
-
}
|
|
342
|
-
async count(collection, query, schemaDefinition) {
|
|
343
|
-
const model = this.getModel(collection, schemaDefinition);
|
|
344
|
-
const q = this.convertQueryFieldsToObjectId(query, schemaDefinition);
|
|
345
|
-
return model.countDocuments(q).exec();
|
|
346
|
-
}
|
|
347
|
-
async update(collection, id, data, schemaDefinition, includePrivateFields) {
|
|
348
|
-
const model = this.getModel(collection, schemaDefinition);
|
|
349
|
-
const { _id } = this.convertQueryFieldsToObjectId({ id }, schemaDefinition);
|
|
350
|
-
const updated = await model
|
|
351
|
-
.findByIdAndUpdate(_id, data, { new: true, runValidators: true })
|
|
352
|
-
.exec();
|
|
353
|
-
if (!updated) {
|
|
354
|
-
throw new Error(`Document with id ${id} not found in collection '${collection}'`);
|
|
355
|
-
}
|
|
356
|
-
return this.toAppObject(updated, includePrivateFields);
|
|
357
|
-
}
|
|
358
|
-
async delete(collection, id, schemaDefinition, includePrivateFields = false) {
|
|
359
|
-
const model = this.getModel(collection, schemaDefinition);
|
|
360
|
-
const { _id } = this.convertQueryFieldsToObjectId({ id }, schemaDefinition);
|
|
361
|
-
const deleted = await model.findByIdAndDelete(_id).exec();
|
|
362
|
-
if (!deleted) {
|
|
363
|
-
throw new Error(`Document with id ${id} not found in collection '${collection}'`);
|
|
364
|
-
}
|
|
365
|
-
return this.toAppObject(deleted, includePrivateFields);
|
|
366
|
-
}
|
|
367
|
-
getNativeCollectionByModelName(modelName) {
|
|
368
|
-
const s = this.getSchema(modelName);
|
|
369
|
-
if (!s)
|
|
370
|
-
throw new Error(`Schema not found for ${modelName}`);
|
|
371
|
-
const collName = s.collection?.toLowerCase?.() ?? s.modelName.toLowerCase();
|
|
372
|
-
// returns the MongoDB driver's Collection (not a Mongoose Model)
|
|
373
|
-
return this.connection.collection(collName);
|
|
374
|
-
}
|
|
375
|
-
/** Create/drop single-field indexes based on schema plan.
|
|
376
|
-
* Only touches indexes named with the prefix below (won't touch user indexes).
|
|
377
|
-
*/
|
|
378
|
-
managedIndexName(field) {
|
|
379
|
-
return `nextmin_idx_${field}`;
|
|
380
|
-
}
|
|
381
|
-
async syncIndexes(modelName, spec) {
|
|
382
|
-
const col = this.getNativeCollectionByModelName(modelName);
|
|
383
|
-
const existing = await col.indexes(); // [{ name, key, unique, sparse, ... }]
|
|
384
|
-
// desired => only from 'spec'
|
|
385
|
-
const desired = Object.entries(spec).map(([field, dir]) => ({
|
|
386
|
-
name: `${field}_${dir === 1 ? 'asc' : 'desc'}`,
|
|
387
|
-
key: { [field]: dir },
|
|
388
|
-
}));
|
|
389
|
-
const desiredNames = new Set(desired.map((d) => d.name));
|
|
390
|
-
// drop indexes not in desired (except _id_)
|
|
391
|
-
for (const idx of existing) {
|
|
392
|
-
if (idx.name === '_id_')
|
|
393
|
-
continue;
|
|
394
|
-
if (!desiredNames.has(idx.name)) {
|
|
395
|
-
try {
|
|
396
|
-
await col.dropIndex(idx.name);
|
|
397
|
-
}
|
|
398
|
-
catch { }
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
// create missing
|
|
402
|
-
const existingNames = new Set((await col.indexes()).map((i) => i.name));
|
|
403
|
-
for (const d of desired) {
|
|
404
|
-
if (!existingNames.has(d.name)) {
|
|
405
|
-
await col.createIndex(d.key, { name: d.name });
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
exports.MongoAdapter = MongoAdapter;
|