@atscript/moost-mongo 0.0.20 → 0.0.22
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/dist/index.cjs +55 -11
- package/dist/index.d.ts +16 -1
- package/dist/index.mjs +55 -11
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -59,7 +59,7 @@ let SelectControlDto = class SelectControlDto$1 {};
|
|
|
59
59
|
_define_property$1(SelectControlDto, "__is_atscript_annotated_type", true);
|
|
60
60
|
_define_property$1(SelectControlDto, "type", {});
|
|
61
61
|
_define_property$1(SelectControlDto, "metadata", new Map());
|
|
62
|
-
(0, __atscript_typescript.defineAnnotatedType)("object", QueryControlsDto).prop("$skip", (0, __atscript_typescript.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$limit", (0, __atscript_typescript.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$count", (0, __atscript_typescript.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type).prop("$sort", (0, __atscript_typescript.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript.defineAnnotatedType)().refTo(SelectControlDto).optional().$type);
|
|
62
|
+
(0, __atscript_typescript.defineAnnotatedType)("object", QueryControlsDto).prop("$skip", (0, __atscript_typescript.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$limit", (0, __atscript_typescript.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$count", (0, __atscript_typescript.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type).prop("$sort", (0, __atscript_typescript.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript.defineAnnotatedType)().refTo(SelectControlDto).optional().$type).prop("$search", (0, __atscript_typescript.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, __atscript_typescript.defineAnnotatedType)().designType("string").tags("string").optional().$type);
|
|
63
63
|
(0, __atscript_typescript.defineAnnotatedType)("object", PagesControlsDto).prop("$page", (0, __atscript_typescript.defineAnnotatedType)().designType("string").tags("string").annotate("expect.pattern", {
|
|
64
64
|
pattern: "^\\d+$",
|
|
65
65
|
flags: "u",
|
|
@@ -68,7 +68,7 @@ _define_property$1(SelectControlDto, "metadata", new Map());
|
|
|
68
68
|
pattern: "^\\d+$",
|
|
69
69
|
flags: "u",
|
|
70
70
|
message: "Expected positive number"
|
|
71
|
-
}, true).optional().$type).prop("$sort", (0, __atscript_typescript.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript.defineAnnotatedType)().refTo(SelectControlDto).optional().$type);
|
|
71
|
+
}, true).optional().$type).prop("$sort", (0, __atscript_typescript.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript.defineAnnotatedType)().refTo(SelectControlDto).optional().$type).prop("$search", (0, __atscript_typescript.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, __atscript_typescript.defineAnnotatedType)().designType("string").tags("string").optional().$type);
|
|
72
72
|
(0, __atscript_typescript.defineAnnotatedType)("object", GetOneControlsDto).prop("$select", (0, __atscript_typescript.defineAnnotatedType)().refTo(SelectControlDto).optional().$type);
|
|
73
73
|
(0, __atscript_typescript.defineAnnotatedType)("object", SortControlDto).propPattern(/./, (0, __atscript_typescript.defineAnnotatedType)("union").item((0, __atscript_typescript.defineAnnotatedType)().designType("number").value(1).$type).item((0, __atscript_typescript.defineAnnotatedType)().designType("number").value(-1).$type).$type);
|
|
74
74
|
(0, __atscript_typescript.defineAnnotatedType)("object", SelectControlDto).propPattern(/./, (0, __atscript_typescript.defineAnnotatedType)("union").item((0, __atscript_typescript.defineAnnotatedType)().designType("number").value(1).$type).item((0, __atscript_typescript.defineAnnotatedType)().designType("number").value(0).$type).$type);
|
|
@@ -206,6 +206,14 @@ var AsMongoController = class {
|
|
|
206
206
|
return projection;
|
|
207
207
|
}
|
|
208
208
|
/**
|
|
209
|
+
* Allows subclasses to transform or modify the filter before querying.
|
|
210
|
+
*
|
|
211
|
+
* @param filter - The original filter object.
|
|
212
|
+
* @returns The transformed filter object (may return `Promise`).
|
|
213
|
+
*/ transformFilter(filter) {
|
|
214
|
+
return filter;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
209
217
|
* Builds MongoDB `FindOptions` object out of URLQL controls.
|
|
210
218
|
*
|
|
211
219
|
* @param controls - Parsed `controls` object.
|
|
@@ -218,6 +226,25 @@ var AsMongoController = class {
|
|
|
218
226
|
};
|
|
219
227
|
}
|
|
220
228
|
/**
|
|
229
|
+
* Prepares a MongoDB $search stage for text-based searching.
|
|
230
|
+
*
|
|
231
|
+
* @param searchTerm - The text string to search for. If not provided, no search is performed.
|
|
232
|
+
* @param indexName - The name of the Atlas Search index to use. If not provided, the default index is used.
|
|
233
|
+
* @returns A $search pipeline stage or an error string if the index is not found. Returns undefined if no searchTerm is provided.
|
|
234
|
+
*/ prepareSearch(searchTerm, indexName) {
|
|
235
|
+
if (!searchTerm) return undefined;
|
|
236
|
+
const index = this.asCollection.getSearchIndex(indexName);
|
|
237
|
+
if (!index) return indexName ? `Search index "${indexName}" does not exist` : "No search index found";
|
|
238
|
+
if (!index.key) return `Invalid index definition: missing index key`;
|
|
239
|
+
return { $search: {
|
|
240
|
+
index: index.key,
|
|
241
|
+
text: {
|
|
242
|
+
query: searchTerm,
|
|
243
|
+
path: { wildcard: "*" }
|
|
244
|
+
}
|
|
245
|
+
} };
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
221
248
|
* **GET /query** – returns an array of documents or a count depending on
|
|
222
249
|
* presence of `$count` control.
|
|
223
250
|
*
|
|
@@ -228,7 +255,19 @@ var AsMongoController = class {
|
|
|
228
255
|
const parsed = (0, urlql.parseUrlql)(query);
|
|
229
256
|
const error = await this.validateUrlql(parsed, "query");
|
|
230
257
|
if (error) return error;
|
|
231
|
-
|
|
258
|
+
if (parsed.controls.$count) return this.asCollection.collection.countDocuments(parsed.filter);
|
|
259
|
+
const search = this.prepareSearch(parsed.controls.$search, parsed.controls.$index);
|
|
260
|
+
if (typeof search === "string") return new __moostjs_event_http.HttpError(400, search);
|
|
261
|
+
const { projection, sort, limit, skip } = this.prepareQueryOptions(parsed.controls);
|
|
262
|
+
const pipeline = [];
|
|
263
|
+
if (search) pipeline.push(search);
|
|
264
|
+
pipeline.push({ $match: this.transformFilter(parsed.filter) });
|
|
265
|
+
if (sort) pipeline.push({ $sort: sort });
|
|
266
|
+
if (skip) pipeline.push({ $skip: skip });
|
|
267
|
+
if (limit) pipeline.push({ $limit: limit });
|
|
268
|
+
else pipeline.push({ $limit: 1e3 });
|
|
269
|
+
if (projection) pipeline.push({ $project: projection });
|
|
270
|
+
return this.asCollection.collection.aggregate(pipeline).toArray();
|
|
232
271
|
}
|
|
233
272
|
/**
|
|
234
273
|
* **GET /pages** – returns paginated documents plus basic pagination meta.
|
|
@@ -240,19 +279,24 @@ var AsMongoController = class {
|
|
|
240
279
|
const parsed = (0, urlql.parseUrlql)(query);
|
|
241
280
|
const error = await this.validateUrlql(parsed, "pages");
|
|
242
281
|
if (error) return error;
|
|
282
|
+
const search = this.prepareSearch(parsed.controls.$search, parsed.controls.$index);
|
|
283
|
+
if (typeof search === "string") return new __moostjs_event_http.HttpError(400, search);
|
|
243
284
|
const controls = parsed.controls;
|
|
244
285
|
const page = Math.max(Number(controls.$page || 1), 1);
|
|
245
286
|
const size = Math.max(Number(controls.$size || 10), 1);
|
|
246
287
|
const skip = (page - 1) * size;
|
|
247
|
-
const
|
|
288
|
+
const pipeline = [];
|
|
289
|
+
if (search) pipeline.push(search);
|
|
290
|
+
pipeline.push({ $match: this.transformFilter(parsed.filter) }, { $facet: {
|
|
248
291
|
documents: [
|
|
249
292
|
controls.$sort ? { $sort: controls.$sort } : undefined,
|
|
250
293
|
{ $skip: skip },
|
|
251
294
|
{ $limit: size },
|
|
252
|
-
controls.$select ? { $project: controls.$select } : undefined
|
|
295
|
+
controls.$select ? { $project: this.transformProjection(controls.$select) } : undefined
|
|
253
296
|
].filter(Boolean),
|
|
254
297
|
meta: [{ $count: "count" }]
|
|
255
|
-
} }
|
|
298
|
+
} });
|
|
299
|
+
const result = await this.asCollection.collection.aggregate(pipeline).toArray();
|
|
256
300
|
const totalDocuments = result[0].meta[0].count;
|
|
257
301
|
return {
|
|
258
302
|
documents: result[0].documents,
|
|
@@ -278,11 +322,11 @@ var AsMongoController = class {
|
|
|
278
322
|
if (Object.keys(parsed.filter).length) return new __moostjs_event_http.HttpError(400, "Filtering is not allowed for \"one\" endpoint");
|
|
279
323
|
const error = await this.validateUrlql(parsed, "getOne");
|
|
280
324
|
if (error) return error;
|
|
281
|
-
if (idValidator?.validate(id, true)) return this.returnOne(this.asCollection.collection.find({ _id: this.asCollection.prepareId(id) }, this.prepareQueryOptions(parsed.controls)).toArray());
|
|
325
|
+
if (idValidator?.validate(id, true)) return this.returnOne(this.asCollection.collection.find(this.transformFilter({ _id: this.asCollection.prepareId(id) }), this.prepareQueryOptions(parsed.controls)).toArray());
|
|
282
326
|
else if (this.asCollection.uniqueProps.size > 0) {
|
|
283
327
|
const filter = [];
|
|
284
328
|
for (const prop of this.asCollection.uniqueProps) filter.push({ [prop]: id });
|
|
285
|
-
return this.returnOne(this.asCollection.collection.find({ $or: filter }, this.prepareQueryOptions(parsed.controls)).toArray());
|
|
329
|
+
return this.returnOne(this.asCollection.collection.find(this.transformFilter({ $or: filter }), this.prepareQueryOptions(parsed.controls)).toArray());
|
|
286
330
|
}
|
|
287
331
|
if (idValidator) return new __atscript_typescript.ValidatorError(idValidator.errors);
|
|
288
332
|
return new __moostjs_event_http.HttpError(500, "Unknown error");
|
|
@@ -325,7 +369,7 @@ else return new __moostjs_event_http.HttpError(500, "Not saved");
|
|
|
325
369
|
*/ async replace(payload) {
|
|
326
370
|
const args = this.asCollection.prepareReplace(payload).toArgs();
|
|
327
371
|
const newData = await this.onWrite("replace", args[1], args[2]);
|
|
328
|
-
if (newData) return this.asCollection.collection.replaceOne(args[0], newData, args[2]);
|
|
372
|
+
if (newData) return this.asCollection.collection.replaceOne(this.transformFilter(args[0]), newData, args[2]);
|
|
329
373
|
return new __moostjs_event_http.HttpError(500, "Not saved");
|
|
330
374
|
}
|
|
331
375
|
/**
|
|
@@ -335,7 +379,7 @@ else return new __moostjs_event_http.HttpError(500, "Not saved");
|
|
|
335
379
|
*/ async update(payload) {
|
|
336
380
|
const args = this.asCollection.prepareUpdate(payload).toArgs();
|
|
337
381
|
const newData = await this.onWrite("update", args[1], args[2]);
|
|
338
|
-
if (newData) return this.asCollection.collection.updateOne(args[0], newData, args[2]);
|
|
382
|
+
if (newData) return this.asCollection.collection.updateOne(this.transformFilter(args[0]), newData, args[2]);
|
|
339
383
|
return new __moostjs_event_http.HttpError(500, "Not saved");
|
|
340
384
|
}
|
|
341
385
|
/**
|
|
@@ -346,7 +390,7 @@ else return new __moostjs_event_http.HttpError(500, "Not saved");
|
|
|
346
390
|
const opts = {};
|
|
347
391
|
id = await this.onRemove(id, opts);
|
|
348
392
|
if (id !== undefined) {
|
|
349
|
-
const result = await this.asCollection.collection.deleteOne({ _id: this.asCollection.prepareId(id) }, opts);
|
|
393
|
+
const result = await this.asCollection.collection.deleteOne(this.transformFilter({ _id: this.asCollection.prepareId(id) }), opts);
|
|
350
394
|
if (result.deletedCount < 1) throw new __moostjs_event_http.HttpError(404);
|
|
351
395
|
return result;
|
|
352
396
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { TConsoleBase, Moost } from 'moost';
|
|
|
3
3
|
import { UrlqlQuery } from 'urlql';
|
|
4
4
|
import { AsMongo, AsCollection } from '@atscript/mongo';
|
|
5
5
|
import { TAtscriptAnnotatedTypeConstructor, ValidatorError } from '@atscript/typescript';
|
|
6
|
-
import { WithId, InsertOneResult, InsertManyResult, UpdateResult, DeleteResult, DeleteOptions, ObjectId, OptionalUnlessRequiredId, InsertOneOptions, BulkWriteOptions, WithoutId, ReplaceOptions, UpdateFilter, UpdateOptions } from 'mongodb';
|
|
6
|
+
import { Document, Filter, WithId, InsertOneResult, InsertManyResult, UpdateResult, DeleteResult, DeleteOptions, ObjectId, OptionalUnlessRequiredId, InsertOneOptions, BulkWriteOptions, WithoutId, ReplaceOptions, UpdateFilter, UpdateOptions } from 'mongodb';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Generic **Moost** controller that exposes a full REST‑style CRUD surface over a
|
|
@@ -106,6 +106,13 @@ declare class AsMongoController<T extends TAtscriptAnnotatedTypeConstructor> {
|
|
|
106
106
|
* @returns Adjusted projection (may return `Promise`).
|
|
107
107
|
*/
|
|
108
108
|
protected transformProjection(projection?: Record<string, 0 | 1>): Record<string, 1> | Record<string, 0> | undefined | Promise<Record<string, 1> | Record<string, 0> | undefined>;
|
|
109
|
+
/**
|
|
110
|
+
* Allows subclasses to transform or modify the filter before querying.
|
|
111
|
+
*
|
|
112
|
+
* @param filter - The original filter object.
|
|
113
|
+
* @returns The transformed filter object (may return `Promise`).
|
|
114
|
+
*/
|
|
115
|
+
protected transformFilter(filter: Document): Filter<InstanceType<T>>;
|
|
109
116
|
/**
|
|
110
117
|
* Builds MongoDB `FindOptions` object out of URLQL controls.
|
|
111
118
|
*
|
|
@@ -117,6 +124,14 @@ declare class AsMongoController<T extends TAtscriptAnnotatedTypeConstructor> {
|
|
|
117
124
|
limit: number | undefined;
|
|
118
125
|
skip: number | undefined;
|
|
119
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* Prepares a MongoDB $search stage for text-based searching.
|
|
129
|
+
*
|
|
130
|
+
* @param searchTerm - The text string to search for. If not provided, no search is performed.
|
|
131
|
+
* @param indexName - The name of the Atlas Search index to use. If not provided, the default index is used.
|
|
132
|
+
* @returns A $search pipeline stage or an error string if the index is not found. Returns undefined if no searchTerm is provided.
|
|
133
|
+
*/
|
|
134
|
+
protected prepareSearch(searchTerm?: string, indexName?: string): string | undefined | Document;
|
|
120
135
|
/**
|
|
121
136
|
* **GET /query** – returns an array of documents or a count depending on
|
|
122
137
|
* presence of `$count` control.
|
package/dist/index.mjs
CHANGED
|
@@ -35,7 +35,7 @@ let SelectControlDto = class SelectControlDto$1 {};
|
|
|
35
35
|
_define_property$1(SelectControlDto, "__is_atscript_annotated_type", true);
|
|
36
36
|
_define_property$1(SelectControlDto, "type", {});
|
|
37
37
|
_define_property$1(SelectControlDto, "metadata", new Map());
|
|
38
|
-
defineAnnotatedType("object", QueryControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$count", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType().refTo(SelectControlDto).optional().$type);
|
|
38
|
+
defineAnnotatedType("object", QueryControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.min", 0).annotate("expect.int", true).optional().$type).prop("$count", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType().refTo(SelectControlDto).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type);
|
|
39
39
|
defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedType().designType("string").tags("string").annotate("expect.pattern", {
|
|
40
40
|
pattern: "^\\d+$",
|
|
41
41
|
flags: "u",
|
|
@@ -44,7 +44,7 @@ defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedTyp
|
|
|
44
44
|
pattern: "^\\d+$",
|
|
45
45
|
flags: "u",
|
|
46
46
|
message: "Expected positive number"
|
|
47
|
-
}, true).optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType().refTo(SelectControlDto).optional().$type);
|
|
47
|
+
}, true).optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType().refTo(SelectControlDto).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type);
|
|
48
48
|
defineAnnotatedType("object", GetOneControlsDto).prop("$select", defineAnnotatedType().refTo(SelectControlDto).optional().$type);
|
|
49
49
|
defineAnnotatedType("object", SortControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("number").value(1).$type).item(defineAnnotatedType().designType("number").value(-1).$type).$type);
|
|
50
50
|
defineAnnotatedType("object", SelectControlDto).propPattern(/./, defineAnnotatedType("union").item(defineAnnotatedType().designType("number").value(1).$type).item(defineAnnotatedType().designType("number").value(0).$type).$type);
|
|
@@ -182,6 +182,14 @@ var AsMongoController = class {
|
|
|
182
182
|
return projection;
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
|
+
* Allows subclasses to transform or modify the filter before querying.
|
|
186
|
+
*
|
|
187
|
+
* @param filter - The original filter object.
|
|
188
|
+
* @returns The transformed filter object (may return `Promise`).
|
|
189
|
+
*/ transformFilter(filter) {
|
|
190
|
+
return filter;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
185
193
|
* Builds MongoDB `FindOptions` object out of URLQL controls.
|
|
186
194
|
*
|
|
187
195
|
* @param controls - Parsed `controls` object.
|
|
@@ -194,6 +202,25 @@ var AsMongoController = class {
|
|
|
194
202
|
};
|
|
195
203
|
}
|
|
196
204
|
/**
|
|
205
|
+
* Prepares a MongoDB $search stage for text-based searching.
|
|
206
|
+
*
|
|
207
|
+
* @param searchTerm - The text string to search for. If not provided, no search is performed.
|
|
208
|
+
* @param indexName - The name of the Atlas Search index to use. If not provided, the default index is used.
|
|
209
|
+
* @returns A $search pipeline stage or an error string if the index is not found. Returns undefined if no searchTerm is provided.
|
|
210
|
+
*/ prepareSearch(searchTerm, indexName) {
|
|
211
|
+
if (!searchTerm) return undefined;
|
|
212
|
+
const index = this.asCollection.getSearchIndex(indexName);
|
|
213
|
+
if (!index) return indexName ? `Search index "${indexName}" does not exist` : "No search index found";
|
|
214
|
+
if (!index.key) return `Invalid index definition: missing index key`;
|
|
215
|
+
return { $search: {
|
|
216
|
+
index: index.key,
|
|
217
|
+
text: {
|
|
218
|
+
query: searchTerm,
|
|
219
|
+
path: { wildcard: "*" }
|
|
220
|
+
}
|
|
221
|
+
} };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
197
224
|
* **GET /query** – returns an array of documents or a count depending on
|
|
198
225
|
* presence of `$count` control.
|
|
199
226
|
*
|
|
@@ -204,7 +231,19 @@ var AsMongoController = class {
|
|
|
204
231
|
const parsed = parseUrlql(query);
|
|
205
232
|
const error = await this.validateUrlql(parsed, "query");
|
|
206
233
|
if (error) return error;
|
|
207
|
-
|
|
234
|
+
if (parsed.controls.$count) return this.asCollection.collection.countDocuments(parsed.filter);
|
|
235
|
+
const search = this.prepareSearch(parsed.controls.$search, parsed.controls.$index);
|
|
236
|
+
if (typeof search === "string") return new HttpError(400, search);
|
|
237
|
+
const { projection, sort, limit, skip } = this.prepareQueryOptions(parsed.controls);
|
|
238
|
+
const pipeline = [];
|
|
239
|
+
if (search) pipeline.push(search);
|
|
240
|
+
pipeline.push({ $match: this.transformFilter(parsed.filter) });
|
|
241
|
+
if (sort) pipeline.push({ $sort: sort });
|
|
242
|
+
if (skip) pipeline.push({ $skip: skip });
|
|
243
|
+
if (limit) pipeline.push({ $limit: limit });
|
|
244
|
+
else pipeline.push({ $limit: 1e3 });
|
|
245
|
+
if (projection) pipeline.push({ $project: projection });
|
|
246
|
+
return this.asCollection.collection.aggregate(pipeline).toArray();
|
|
208
247
|
}
|
|
209
248
|
/**
|
|
210
249
|
* **GET /pages** – returns paginated documents plus basic pagination meta.
|
|
@@ -216,19 +255,24 @@ var AsMongoController = class {
|
|
|
216
255
|
const parsed = parseUrlql(query);
|
|
217
256
|
const error = await this.validateUrlql(parsed, "pages");
|
|
218
257
|
if (error) return error;
|
|
258
|
+
const search = this.prepareSearch(parsed.controls.$search, parsed.controls.$index);
|
|
259
|
+
if (typeof search === "string") return new HttpError(400, search);
|
|
219
260
|
const controls = parsed.controls;
|
|
220
261
|
const page = Math.max(Number(controls.$page || 1), 1);
|
|
221
262
|
const size = Math.max(Number(controls.$size || 10), 1);
|
|
222
263
|
const skip = (page - 1) * size;
|
|
223
|
-
const
|
|
264
|
+
const pipeline = [];
|
|
265
|
+
if (search) pipeline.push(search);
|
|
266
|
+
pipeline.push({ $match: this.transformFilter(parsed.filter) }, { $facet: {
|
|
224
267
|
documents: [
|
|
225
268
|
controls.$sort ? { $sort: controls.$sort } : undefined,
|
|
226
269
|
{ $skip: skip },
|
|
227
270
|
{ $limit: size },
|
|
228
|
-
controls.$select ? { $project: controls.$select } : undefined
|
|
271
|
+
controls.$select ? { $project: this.transformProjection(controls.$select) } : undefined
|
|
229
272
|
].filter(Boolean),
|
|
230
273
|
meta: [{ $count: "count" }]
|
|
231
|
-
} }
|
|
274
|
+
} });
|
|
275
|
+
const result = await this.asCollection.collection.aggregate(pipeline).toArray();
|
|
232
276
|
const totalDocuments = result[0].meta[0].count;
|
|
233
277
|
return {
|
|
234
278
|
documents: result[0].documents,
|
|
@@ -254,11 +298,11 @@ var AsMongoController = class {
|
|
|
254
298
|
if (Object.keys(parsed.filter).length) return new HttpError(400, "Filtering is not allowed for \"one\" endpoint");
|
|
255
299
|
const error = await this.validateUrlql(parsed, "getOne");
|
|
256
300
|
if (error) return error;
|
|
257
|
-
if (idValidator?.validate(id, true)) return this.returnOne(this.asCollection.collection.find({ _id: this.asCollection.prepareId(id) }, this.prepareQueryOptions(parsed.controls)).toArray());
|
|
301
|
+
if (idValidator?.validate(id, true)) return this.returnOne(this.asCollection.collection.find(this.transformFilter({ _id: this.asCollection.prepareId(id) }), this.prepareQueryOptions(parsed.controls)).toArray());
|
|
258
302
|
else if (this.asCollection.uniqueProps.size > 0) {
|
|
259
303
|
const filter = [];
|
|
260
304
|
for (const prop of this.asCollection.uniqueProps) filter.push({ [prop]: id });
|
|
261
|
-
return this.returnOne(this.asCollection.collection.find({ $or: filter }, this.prepareQueryOptions(parsed.controls)).toArray());
|
|
305
|
+
return this.returnOne(this.asCollection.collection.find(this.transformFilter({ $or: filter }), this.prepareQueryOptions(parsed.controls)).toArray());
|
|
262
306
|
}
|
|
263
307
|
if (idValidator) return new ValidatorError(idValidator.errors);
|
|
264
308
|
return new HttpError(500, "Unknown error");
|
|
@@ -301,7 +345,7 @@ else return new HttpError(500, "Not saved");
|
|
|
301
345
|
*/ async replace(payload) {
|
|
302
346
|
const args = this.asCollection.prepareReplace(payload).toArgs();
|
|
303
347
|
const newData = await this.onWrite("replace", args[1], args[2]);
|
|
304
|
-
if (newData) return this.asCollection.collection.replaceOne(args[0], newData, args[2]);
|
|
348
|
+
if (newData) return this.asCollection.collection.replaceOne(this.transformFilter(args[0]), newData, args[2]);
|
|
305
349
|
return new HttpError(500, "Not saved");
|
|
306
350
|
}
|
|
307
351
|
/**
|
|
@@ -311,7 +355,7 @@ else return new HttpError(500, "Not saved");
|
|
|
311
355
|
*/ async update(payload) {
|
|
312
356
|
const args = this.asCollection.prepareUpdate(payload).toArgs();
|
|
313
357
|
const newData = await this.onWrite("update", args[1], args[2]);
|
|
314
|
-
if (newData) return this.asCollection.collection.updateOne(args[0], newData, args[2]);
|
|
358
|
+
if (newData) return this.asCollection.collection.updateOne(this.transformFilter(args[0]), newData, args[2]);
|
|
315
359
|
return new HttpError(500, "Not saved");
|
|
316
360
|
}
|
|
317
361
|
/**
|
|
@@ -322,7 +366,7 @@ else return new HttpError(500, "Not saved");
|
|
|
322
366
|
const opts = {};
|
|
323
367
|
id = await this.onRemove(id, opts);
|
|
324
368
|
if (id !== undefined) {
|
|
325
|
-
const result = await this.asCollection.collection.deleteOne({ _id: this.asCollection.prepareId(id) }, opts);
|
|
369
|
+
const result = await this.asCollection.collection.deleteOne(this.transformFilter({ _id: this.asCollection.prepareId(id) }), opts);
|
|
326
370
|
if (result.deletedCount < 1) throw new HttpError(404);
|
|
327
371
|
return result;
|
|
328
372
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/moost-mongo",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"description": "Atscript Mongo for Moost.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@moostjs/event-http": "^0.5.31",
|
|
45
45
|
"mongodb": "^6.17.0",
|
|
46
46
|
"moost": "^0.5.31",
|
|
47
|
-
"@atscript/mongo": "^0.0.
|
|
48
|
-
"@atscript/typescript": "^0.0.
|
|
47
|
+
"@atscript/mongo": "^0.0.22",
|
|
48
|
+
"@atscript/typescript": "^0.0.22"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"pub": "pnpm publish --access public",
|