@travetto/model-mongo 2.0.3 → 2.1.2
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 +8 -7
- package/package.json +5 -6
- package/src/config.ts +8 -7
- package/src/internal/util.ts +4 -3
- package/src/service.ts +26 -19
package/README.md
CHANGED
|
@@ -92,8 +92,6 @@ export class MongoModelConfig {
|
|
|
92
92
|
*/
|
|
93
93
|
@Field(Object)
|
|
94
94
|
options: mongo.MongoClientOptions = {
|
|
95
|
-
useNewUrlParser: true,
|
|
96
|
-
useUnifiedTopology: true
|
|
97
95
|
};
|
|
98
96
|
|
|
99
97
|
/**
|
|
@@ -111,7 +109,10 @@ export class MongoModelConfig {
|
|
|
111
109
|
*/
|
|
112
110
|
async fetch(val: string) {
|
|
113
111
|
return ResourceManager.read(val)
|
|
114
|
-
.
|
|
112
|
+
.then(res => typeof res === 'string' ? res : res.toString('utf8'))
|
|
113
|
+
.catch(e => fs.readFile(val)
|
|
114
|
+
.then(res => typeof res === 'string' ? res : res.toString('utf8'))
|
|
115
|
+
)
|
|
115
116
|
.catch(() => val);
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -122,16 +123,16 @@ export class MongoModelConfig {
|
|
|
122
123
|
const opts = this.options;
|
|
123
124
|
if (opts.ssl) {
|
|
124
125
|
if (opts.sslCert) {
|
|
125
|
-
opts.
|
|
126
|
+
opts.tlsCertificateFile = await this.fetch(opts.sslCert);
|
|
126
127
|
}
|
|
127
128
|
if (opts.sslKey) {
|
|
128
|
-
opts.sslKey = await this.fetch(opts.sslKey
|
|
129
|
+
opts.sslKey = await this.fetch(opts.sslKey);
|
|
129
130
|
}
|
|
130
131
|
if (opts.sslCA) {
|
|
131
|
-
opts.sslCA = await
|
|
132
|
+
opts.sslCA = await this.fetch(opts.sslCA);
|
|
132
133
|
}
|
|
133
134
|
if (opts.sslCRL) {
|
|
134
|
-
opts.sslCRL = await
|
|
135
|
+
opts.sslCRL = await this.fetch(opts.sslCRL);
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
138
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-mongo",
|
|
3
3
|
"displayName": "MongoDB Model Support",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.2",
|
|
5
5
|
"description": "Mongo backing for the travetto model module.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mongo",
|
|
@@ -26,11 +26,10 @@
|
|
|
26
26
|
"directory": "module/model-mongo"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/config": "^2.
|
|
30
|
-
"@travetto/model": "^2.
|
|
31
|
-
"@travetto/model-query": "2.
|
|
32
|
-
"
|
|
33
|
-
"mongodb": "^3.6.9"
|
|
29
|
+
"@travetto/config": "^2.1.2",
|
|
30
|
+
"@travetto/model": "^2.1.2",
|
|
31
|
+
"@travetto/model-query": "2.1.2",
|
|
32
|
+
"mongodb": "^4.7.0"
|
|
34
33
|
},
|
|
35
34
|
"publishConfig": {
|
|
36
35
|
"access": "public"
|
package/src/config.ts
CHANGED
|
@@ -44,8 +44,6 @@ export class MongoModelConfig {
|
|
|
44
44
|
*/
|
|
45
45
|
@Field(Object)
|
|
46
46
|
options: mongo.MongoClientOptions = {
|
|
47
|
-
useNewUrlParser: true,
|
|
48
|
-
useUnifiedTopology: true
|
|
49
47
|
};
|
|
50
48
|
|
|
51
49
|
/**
|
|
@@ -63,7 +61,10 @@ export class MongoModelConfig {
|
|
|
63
61
|
*/
|
|
64
62
|
async fetch(val: string) {
|
|
65
63
|
return ResourceManager.read(val)
|
|
66
|
-
.
|
|
64
|
+
.then(res => typeof res === 'string' ? res : res.toString('utf8'))
|
|
65
|
+
.catch(e => fs.readFile(val)
|
|
66
|
+
.then(res => typeof res === 'string' ? res : res.toString('utf8'))
|
|
67
|
+
)
|
|
67
68
|
.catch(() => val);
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -74,16 +75,16 @@ export class MongoModelConfig {
|
|
|
74
75
|
const opts = this.options;
|
|
75
76
|
if (opts.ssl) {
|
|
76
77
|
if (opts.sslCert) {
|
|
77
|
-
opts.
|
|
78
|
+
opts.tlsCertificateFile = await this.fetch(opts.sslCert);
|
|
78
79
|
}
|
|
79
80
|
if (opts.sslKey) {
|
|
80
|
-
opts.sslKey = await this.fetch(opts.sslKey
|
|
81
|
+
opts.sslKey = await this.fetch(opts.sslKey);
|
|
81
82
|
}
|
|
82
83
|
if (opts.sslCA) {
|
|
83
|
-
opts.sslCA = await
|
|
84
|
+
opts.sslCA = await this.fetch(opts.sslCA);
|
|
84
85
|
}
|
|
85
86
|
if (opts.sslCRL) {
|
|
86
|
-
opts.sslCRL = await
|
|
87
|
+
opts.sslCRL = await this.fetch(opts.sslCRL);
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
}
|
package/src/internal/util.ts
CHANGED
|
@@ -31,17 +31,18 @@ export class MongoUtil {
|
|
|
31
31
|
f = f[key as keyof typeof f] as IndexField<T>;
|
|
32
32
|
keys.push(key);
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
const rf = f as unknown as (number | boolean);
|
|
35
|
+
return { [keys.join('.')]: rf === true ? 1 : rf } as Record<string, number>;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
static uuid(val: string) {
|
|
38
39
|
return new mongo.Binary(Buffer.from(val.replace(/-/g, ''), 'hex'), mongo.Binary.SUBTYPE_UUID);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
static idToString(id: string | mongo.
|
|
42
|
+
static idToString(id: string | mongo.ObjectId | mongo.Binary) {
|
|
42
43
|
if (typeof id === 'string') {
|
|
43
44
|
return id;
|
|
44
|
-
} else if (id instanceof mongo.
|
|
45
|
+
} else if (id instanceof mongo.ObjectId) {
|
|
45
46
|
return id.toHexString();
|
|
46
47
|
} else {
|
|
47
48
|
return id.buffer.toString('hex');
|
package/src/service.ts
CHANGED
|
@@ -33,7 +33,9 @@ import { MongoModelConfig } from './config';
|
|
|
33
33
|
|
|
34
34
|
const IdxFieldsⲐ = Symbol.for('@trv:model-mongo/idx');
|
|
35
35
|
|
|
36
|
-
const asFielded = <T extends ModelType>(cfg: IndexConfig<T>) => (cfg as unknown as { [IdxFieldsⲐ]: mongo.
|
|
36
|
+
const asFielded = <T extends ModelType>(cfg: IndexConfig<T>) => (cfg as unknown as { [IdxFieldsⲐ]: mongo.Sort });
|
|
37
|
+
|
|
38
|
+
type IdxCfg = mongo.CreateIndexesOptions;
|
|
37
39
|
|
|
38
40
|
/**
|
|
39
41
|
* Mongo-based model source
|
|
@@ -53,7 +55,7 @@ export class MongoModelService implements
|
|
|
53
55
|
constructor(public readonly config: MongoModelConfig) { }
|
|
54
56
|
|
|
55
57
|
async #describeStreamRaw(location: string) {
|
|
56
|
-
const files = (await this.#bucket.find({ filename: location }, { limit: 1 }).toArray()) as
|
|
58
|
+
const files = (await this.#bucket.find({ filename: location }, { limit: 1 }).toArray()) as (mongo.GridFSFile & { metadata: StreamMeta })[];
|
|
57
59
|
|
|
58
60
|
if (!files?.length) {
|
|
59
61
|
throw new NotFoundError(STREAMS, location);
|
|
@@ -92,11 +94,11 @@ export class MongoModelService implements
|
|
|
92
94
|
await this.#db.dropDatabase();
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
getGeoIndices<T extends ModelType>(cls: Class<T>, path: FieldConfig[] = [], root = cls):
|
|
97
|
+
getGeoIndices<T extends ModelType>(cls: Class<T>, path: FieldConfig[] = [], root = cls): mongo.IndexSpecification[] {
|
|
96
98
|
const fields = SchemaRegistry.has(cls) ?
|
|
97
99
|
Object.values(SchemaRegistry.get(cls).views[AllViewⲐ].schema) :
|
|
98
100
|
[];
|
|
99
|
-
const out:
|
|
101
|
+
const out: mongo.IndexSpecification[] = [];
|
|
100
102
|
for (const field of fields) {
|
|
101
103
|
if (SchemaRegistry.has(field.type)) {
|
|
102
104
|
// Recurse
|
|
@@ -115,7 +117,10 @@ export class MongoModelService implements
|
|
|
115
117
|
return [
|
|
116
118
|
...indices.map(idx => {
|
|
117
119
|
const combined = asFielded(idx)[IdxFieldsⲐ] ??= Object.assign({}, ...idx.fields.map(x => MongoUtil.toIndex(x as IndexField<T>)));
|
|
118
|
-
return [
|
|
120
|
+
return [
|
|
121
|
+
combined as mongo.IndexSpecification,
|
|
122
|
+
(idx.type === 'unique' ? { unique: true } : {}) as IdxCfg
|
|
123
|
+
] as const;
|
|
119
124
|
}),
|
|
120
125
|
...this.getGeoIndices(cls).map(x => [x] as const)
|
|
121
126
|
];
|
|
@@ -127,7 +132,7 @@ export class MongoModelService implements
|
|
|
127
132
|
if (creating.length) {
|
|
128
133
|
console.debug('Creating indexes', { indices: creating });
|
|
129
134
|
for (const el of creating) {
|
|
130
|
-
await col.createIndex(el[0], el[1]);
|
|
135
|
+
await col.createIndex(el[0], el[1] ?? {});
|
|
131
136
|
}
|
|
132
137
|
}
|
|
133
138
|
}
|
|
@@ -177,7 +182,7 @@ export class MongoModelService implements
|
|
|
177
182
|
|
|
178
183
|
const store = await this.getStore(cls);
|
|
179
184
|
const result = await store.insertOne(cleaned);
|
|
180
|
-
if (result.
|
|
185
|
+
if (!result.insertedId) {
|
|
181
186
|
throw new ExistsError(cls, cleaned.id);
|
|
182
187
|
}
|
|
183
188
|
delete (cleaned as { _id?: unknown })._id;
|
|
@@ -239,7 +244,11 @@ export class MongoModelService implements
|
|
|
239
244
|
}, {} as Record<string, unknown>);
|
|
240
245
|
|
|
241
246
|
const id = item.id;
|
|
242
|
-
const res = await store.findOneAndUpdate(
|
|
247
|
+
const res = await store.findOneAndUpdate(
|
|
248
|
+
this.getWhere<ModelType>(cls, { id }),
|
|
249
|
+
final,
|
|
250
|
+
{ returnDocument: 'after' }
|
|
251
|
+
);
|
|
243
252
|
|
|
244
253
|
if (!res.value) {
|
|
245
254
|
new NotFoundError(cls, id);
|
|
@@ -258,8 +267,7 @@ export class MongoModelService implements
|
|
|
258
267
|
|
|
259
268
|
async * list<T extends ModelType>(cls: Class<T>) {
|
|
260
269
|
const store = await this.getStore(cls);
|
|
261
|
-
const cursor = store.find(this.getWhere(cls, {})).batchSize(100);
|
|
262
|
-
cursor.timeout = true;
|
|
270
|
+
const cursor = store.find(this.getWhere(cls, {}), { timeout: true }).batchSize(100);
|
|
263
271
|
for await (const el of cursor) {
|
|
264
272
|
try {
|
|
265
273
|
yield MongoUtil.postLoadId(await ModelCrudUtil.load(cls, el));
|
|
@@ -323,7 +331,7 @@ export class MongoModelService implements
|
|
|
323
331
|
}
|
|
324
332
|
|
|
325
333
|
const store = await this.getStore(cls);
|
|
326
|
-
const bulk = store.initializeUnorderedBulkOp({ w: 1 });
|
|
334
|
+
const bulk = store.initializeUnorderedBulkOp({ writeConcern: { w: 1 } });
|
|
327
335
|
const { upsertedIds, insertedIds } = await ModelBulkUtil.preStore(cls, operations, this);
|
|
328
336
|
|
|
329
337
|
out.insertedIds = new Map([...upsertedIds.entries(), ...insertedIds.entries()]);
|
|
@@ -336,7 +344,7 @@ export class MongoModelService implements
|
|
|
336
344
|
} else if (op.update) {
|
|
337
345
|
bulk.find({ _id: MongoUtil.uuid(op.update.id) }).update({ $set: op.update });
|
|
338
346
|
} else if (op.delete) {
|
|
339
|
-
bulk.find({ _id: MongoUtil.uuid(op.delete.id) }).
|
|
347
|
+
bulk.find({ _id: MongoUtil.uuid(op.delete.id) }).deleteOne();
|
|
340
348
|
}
|
|
341
349
|
}
|
|
342
350
|
|
|
@@ -347,7 +355,7 @@ export class MongoModelService implements
|
|
|
347
355
|
MongoUtil.postLoadId(op.insert as T);
|
|
348
356
|
}
|
|
349
357
|
}
|
|
350
|
-
for (const { index, _id } of res.getUpsertedIds() as { index: number, _id: mongo.
|
|
358
|
+
for (const { index, _id } of res.getUpsertedIds() as { index: number, _id: mongo.ObjectId }[]) {
|
|
351
359
|
out.insertedIds.set(index, MongoUtil.idToString(_id));
|
|
352
360
|
}
|
|
353
361
|
|
|
@@ -422,10 +430,9 @@ export class MongoModelService implements
|
|
|
422
430
|
const where = this.getWhere(
|
|
423
431
|
cls,
|
|
424
432
|
ModelIndexedUtil.projectIndex(cls, idx, body, { emptySortValue: { $exists: true } }) as WhereClause<T>
|
|
425
|
-
) as mongo.
|
|
433
|
+
) as mongo.Filter<T>;
|
|
426
434
|
|
|
427
|
-
const cursor = store.find(where).batchSize(100).sort(asFielded(idxCfg)[IdxFieldsⲐ]);
|
|
428
|
-
cursor.timeout = true;
|
|
435
|
+
const cursor = store.find(where, { timeout: true }).batchSize(100).sort(asFielded(idxCfg)[IdxFieldsⲐ]);
|
|
429
436
|
|
|
430
437
|
for await (const el of cursor) {
|
|
431
438
|
yield (await MongoUtil.postLoadId(await ModelCrudUtil.load(cls, el))) as T;
|
|
@@ -522,10 +529,10 @@ export class MongoModelService implements
|
|
|
522
529
|
|
|
523
530
|
pipeline.unshift({ $match: q });
|
|
524
531
|
|
|
525
|
-
const result = await col.aggregate(pipeline).toArray();
|
|
532
|
+
const result = (await col.aggregate(pipeline).toArray()) as { _id: mongo.ObjectId, count: number }[];
|
|
526
533
|
|
|
527
|
-
return result.map(
|
|
528
|
-
key: val._id,
|
|
534
|
+
return result.map(val => ({
|
|
535
|
+
key: MongoUtil.idToString(val._id),
|
|
529
536
|
count: val.count
|
|
530
537
|
})).sort((a, b) => b.count - a.count);
|
|
531
538
|
}
|