@geekbears/gb-mongoose-query-parser 1.2.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/.prettierrc +9 -0
- package/LICENSE +21 -0
- package/README.md +420 -0
- package/lib/decorators/api-single-query.decorator.d.ts +1 -0
- package/lib/decorators/api-single-query.decorator.js +25 -0
- package/lib/decorators/api-standard-query.decorator.d.ts +6 -0
- package/lib/decorators/api-standard-query.decorator.js +50 -0
- package/lib/decorators/index.d.ts +2 -0
- package/lib/decorators/index.js +15 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +17 -0
- package/lib/query-parser-helper.d.ts +70 -0
- package/lib/query-parser-helper.js +235 -0
- package/lib/query-parser.d.ts +96 -0
- package/lib/query-parser.js +397 -0
- package/lib/test.spec.d.ts +1 -0
- package/lib/test.spec.js +211 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.js +15 -0
- package/lib/types/parser-options.type.d.ts +18 -0
- package/lib/types/parser-options.type.js +3 -0
- package/lib/types/query-options.type.d.ts +15 -0
- package/lib/types/query-options.type.js +3 -0
- package/package.json +57 -0
- package/tslint.json +62 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (_) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.QueryParser = void 0;
|
|
51
|
+
var mongoose_1 = require("mongoose");
|
|
52
|
+
var lodash_1 = require("lodash");
|
|
53
|
+
var query_parser_1 = require("./query-parser");
|
|
54
|
+
/**
|
|
55
|
+
* Helper class
|
|
56
|
+
* Implements the `MongooseQueryParser` and exposes a single method to execute queries on models
|
|
57
|
+
*/
|
|
58
|
+
var QueryParser = /** @class */ (function () {
|
|
59
|
+
function QueryParser() {
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Build a Mongoose query on a given model from the parsed query string
|
|
63
|
+
* @param model a mongoose `Model` object to perform the query on
|
|
64
|
+
* @param query a query string object received by the controller
|
|
65
|
+
* @param count an optional flag that indicates wether a `count` operation should be performed
|
|
66
|
+
* @returns a Mongoose query on the provided model
|
|
67
|
+
*/
|
|
68
|
+
QueryParser.parseQuery = function (model, query, count) {
|
|
69
|
+
if (count === void 0) { count = false; }
|
|
70
|
+
var docsQuery;
|
|
71
|
+
var countQueryDocuments;
|
|
72
|
+
docsQuery = model.find({});
|
|
73
|
+
countQueryDocuments = count ? model.countDocuments() : null;
|
|
74
|
+
if (!(0, lodash_1.isEmpty)(query)) {
|
|
75
|
+
var parser = new query_parser_1.MongooseQueryParser();
|
|
76
|
+
var queryParams = parser.parse(query);
|
|
77
|
+
(0, lodash_1.forOwn)(transformFilter(queryParams.filter), function (value, key) {
|
|
78
|
+
docsQuery = docsQuery.where(key, value);
|
|
79
|
+
if (countQueryDocuments)
|
|
80
|
+
countQueryDocuments = countQueryDocuments.where(key, value);
|
|
81
|
+
});
|
|
82
|
+
if (queryParams.populate)
|
|
83
|
+
docsQuery = docsQuery.populate(queryParams.populate);
|
|
84
|
+
if (queryParams.deepPopulate)
|
|
85
|
+
docsQuery = docsQuery.populate(queryParams.deepPopulate);
|
|
86
|
+
if (queryParams.sort)
|
|
87
|
+
docsQuery = docsQuery.sort(queryParams.sort);
|
|
88
|
+
if (queryParams.limit)
|
|
89
|
+
docsQuery = docsQuery.limit(queryParams.limit);
|
|
90
|
+
if (queryParams.select)
|
|
91
|
+
docsQuery = docsQuery.select(queryParams.select);
|
|
92
|
+
if (queryParams.skip)
|
|
93
|
+
docsQuery = docsQuery.skip(queryParams.skip);
|
|
94
|
+
}
|
|
95
|
+
return count && countQueryDocuments ? countQueryDocuments : docsQuery;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Get all documents matching the provided query for a given model
|
|
99
|
+
* @param model a mongoose `Model` object to perform the query on
|
|
100
|
+
* @param query a query string object received by the controller
|
|
101
|
+
* @param count an optional flag that indicates wether a `count` operation should be performed
|
|
102
|
+
* @returns the result of executing the provided query on the provided model
|
|
103
|
+
*/
|
|
104
|
+
QueryParser.docByQuery = function (model, query, count) {
|
|
105
|
+
if (count === void 0) { count = false; }
|
|
106
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
107
|
+
var error_1;
|
|
108
|
+
var _a;
|
|
109
|
+
return __generator(this, function (_b) {
|
|
110
|
+
switch (_b.label) {
|
|
111
|
+
case 0:
|
|
112
|
+
_b.trys.push([0, 2, , 3]);
|
|
113
|
+
_a = {};
|
|
114
|
+
return [4 /*yield*/, QueryParser.parseQuery(model, query, count).exec()];
|
|
115
|
+
case 1: return [2 /*return*/, (_a.data = _b.sent(), _a)];
|
|
116
|
+
case 2:
|
|
117
|
+
error_1 = _b.sent();
|
|
118
|
+
throw new Error(error_1);
|
|
119
|
+
case 3: return [2 /*return*/];
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Get all documents matching the provided query for a given model to a mongodb cursor resource
|
|
126
|
+
* @param model a mongoose `Model` object to perform the query on
|
|
127
|
+
* @param query a query string object received by the controller
|
|
128
|
+
* @param count an optional flag that indicates wether a `count` operation should be performed
|
|
129
|
+
* @returns the cursor of executing the provided query on the provided model
|
|
130
|
+
*/
|
|
131
|
+
QueryParser.docByQueryToCursor = function (model, query) {
|
|
132
|
+
try {
|
|
133
|
+
return {
|
|
134
|
+
data: QueryParser.parseQuery(model, query, false).cursor(),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new Error(error);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Build a Mongoose `findOne` query on a given model with additional params
|
|
143
|
+
* @param model a Mongoose `Model` object to perform the query on
|
|
144
|
+
* @param filter a Mongoose filter query on the provided model
|
|
145
|
+
* @param query a query string object received by the controller
|
|
146
|
+
* @returns a Mongoose query on the provided model
|
|
147
|
+
*/
|
|
148
|
+
QueryParser.parseFilterQuery = function (model, filter, query) {
|
|
149
|
+
var docQuery;
|
|
150
|
+
docQuery = model.findOne(__assign({}, filter));
|
|
151
|
+
if (!(0, lodash_1.isEmpty)(query)) {
|
|
152
|
+
var parser = new query_parser_1.MongooseQueryParser({
|
|
153
|
+
blacklist: ['filter', 'sort', 'limit', 'skip'],
|
|
154
|
+
});
|
|
155
|
+
var queryParams = parser.parse(query);
|
|
156
|
+
if (queryParams.populate)
|
|
157
|
+
docQuery = docQuery.populate(queryParams.populate);
|
|
158
|
+
if (queryParams.deepPopulate)
|
|
159
|
+
docQuery = docQuery.populate(queryParams.deepPopulate);
|
|
160
|
+
if (queryParams.select)
|
|
161
|
+
docQuery = docQuery.select(queryParams.select);
|
|
162
|
+
}
|
|
163
|
+
return docQuery;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Get a document matching the provided query for a given model
|
|
167
|
+
* @param model a Mongoose `Model` object to perform the query on
|
|
168
|
+
* @param filter a Mongoose filter query on the provided model
|
|
169
|
+
* @param query a query string object received by the controller
|
|
170
|
+
* @returns the result of executing the provided query on the provided model
|
|
171
|
+
*/
|
|
172
|
+
QueryParser.docByFilter = function (model, filter, query) {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
174
|
+
var error_2;
|
|
175
|
+
var _a;
|
|
176
|
+
return __generator(this, function (_b) {
|
|
177
|
+
switch (_b.label) {
|
|
178
|
+
case 0:
|
|
179
|
+
_b.trys.push([0, 2, , 3]);
|
|
180
|
+
_a = {};
|
|
181
|
+
return [4 /*yield*/, QueryParser.parseFilterQuery(model, filter, query).exec()];
|
|
182
|
+
case 1: return [2 /*return*/, (_a.data = _b.sent(),
|
|
183
|
+
_a)];
|
|
184
|
+
case 2:
|
|
185
|
+
error_2 = _b.sent();
|
|
186
|
+
throw new Error(error_2);
|
|
187
|
+
case 3: return [2 /*return*/];
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Build a Mongoose `findOneById` query on a given model with additional params
|
|
194
|
+
* @param model a Mongoose `Model` object to perform the query on
|
|
195
|
+
* @param _id the resource id
|
|
196
|
+
* @param query a query string object received by the controller
|
|
197
|
+
* @returns a Mongoose query on the provided model
|
|
198
|
+
*/
|
|
199
|
+
QueryParser.parseByIdQuery = function (model, _id, query) {
|
|
200
|
+
return QueryParser.parseFilterQuery(model, { _id: _id }, query);
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Get a document matching the provided query for a given model
|
|
204
|
+
* @param model a Mongoose `Model` object to perform the query on
|
|
205
|
+
* @param _id the resource id
|
|
206
|
+
* @param query a query string object received by the controller
|
|
207
|
+
* @returns the result of executing the provided query on the provided model
|
|
208
|
+
*/
|
|
209
|
+
QueryParser.docById = function (model, _id, query) {
|
|
210
|
+
return QueryParser.docByFilter(model, { _id: _id }, query);
|
|
211
|
+
};
|
|
212
|
+
return QueryParser;
|
|
213
|
+
}());
|
|
214
|
+
exports.QueryParser = QueryParser;
|
|
215
|
+
/**
|
|
216
|
+
* Transforms the filter param to correctly support `ObjectId`s
|
|
217
|
+
* @param filter the filter param
|
|
218
|
+
* @returns a casted filter
|
|
219
|
+
*/
|
|
220
|
+
function transformFilter(filter) {
|
|
221
|
+
var walk = function (obj) {
|
|
222
|
+
for (var key in obj) {
|
|
223
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
224
|
+
var value = obj[key];
|
|
225
|
+
if (typeof value === 'object')
|
|
226
|
+
walk(obj[key]);
|
|
227
|
+
else
|
|
228
|
+
obj[key] = mongoose_1.Types.ObjectId.isValid(value) ? new mongoose_1.Types.ObjectId(value) : value;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return obj;
|
|
232
|
+
};
|
|
233
|
+
return walk(__assign({}, filter));
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=query-parser-helper.js.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ParserOptions } from './types/parser-options.type';
|
|
2
|
+
import { QueryOptions } from './types/query-options.type';
|
|
3
|
+
export declare class MongooseQueryParser {
|
|
4
|
+
private options;
|
|
5
|
+
private readonly defaultDateFormat;
|
|
6
|
+
private readonly builtInCaster;
|
|
7
|
+
private readonly operators;
|
|
8
|
+
constructor(options?: ParserOptions);
|
|
9
|
+
/**
|
|
10
|
+
* parses query string/object to Mongoose friendly query object/QueryOptions
|
|
11
|
+
* @param {string | Object} query
|
|
12
|
+
* @param {Object} [context]
|
|
13
|
+
* @return {QueryOptions}
|
|
14
|
+
*/
|
|
15
|
+
parse(query: string | Object, context?: Object): QueryOptions;
|
|
16
|
+
/**
|
|
17
|
+
* parses string to typed values
|
|
18
|
+
* This methods will apply auto type casting on Number, RegExp, Date, Boolean and null
|
|
19
|
+
* Also, it will apply defined casters in given options of the instance
|
|
20
|
+
* @param {string} value
|
|
21
|
+
* @param {string} key
|
|
22
|
+
* @return {any} typed value
|
|
23
|
+
*/
|
|
24
|
+
parseValue(value: string, key?: string): any;
|
|
25
|
+
private castFilter;
|
|
26
|
+
private parseFilter;
|
|
27
|
+
private parseOperator;
|
|
28
|
+
/**
|
|
29
|
+
* cast select query to object like:
|
|
30
|
+
* select=a,b or select=-a,-b
|
|
31
|
+
* =>
|
|
32
|
+
* {select: { a: 1, b: 1 }} or {select: { a: 0, b: 0 }}
|
|
33
|
+
* @param val
|
|
34
|
+
*/
|
|
35
|
+
private castSelect;
|
|
36
|
+
/**
|
|
37
|
+
* cast populate query to object like:
|
|
38
|
+
* populate=field1.p1,field1.p2,field2
|
|
39
|
+
* =>
|
|
40
|
+
* [{path: 'field1', select: 'p1 p2'}, {path: 'field2'}]
|
|
41
|
+
* @param val
|
|
42
|
+
*/
|
|
43
|
+
private castPopulate;
|
|
44
|
+
/**
|
|
45
|
+
* cast deep populate query to object like:
|
|
46
|
+
* populate={"path":"any", select="deep"}
|
|
47
|
+
* =>
|
|
48
|
+
* {path: 'field1', select: 'deep'}
|
|
49
|
+
* @param deepPopulate
|
|
50
|
+
*/
|
|
51
|
+
private castDeepPopulate;
|
|
52
|
+
private parseDeepPopulate;
|
|
53
|
+
/**
|
|
54
|
+
* cast sort query to object like
|
|
55
|
+
* sort=-a,b
|
|
56
|
+
* =>
|
|
57
|
+
* {sort: {a: -1, b: 1}}
|
|
58
|
+
* @param sort
|
|
59
|
+
*/
|
|
60
|
+
private castSort;
|
|
61
|
+
/**
|
|
62
|
+
* Map/reduce helper to transform list of unaries
|
|
63
|
+
* like '+a,-b,c' to {a: 1, b: -1, c: 1}
|
|
64
|
+
*/
|
|
65
|
+
private parseUnaries;
|
|
66
|
+
/**
|
|
67
|
+
* cast skip query to object like
|
|
68
|
+
* skip=100
|
|
69
|
+
* =>
|
|
70
|
+
* {skip: 100}
|
|
71
|
+
* @param skip
|
|
72
|
+
*/
|
|
73
|
+
private castSkip;
|
|
74
|
+
/**
|
|
75
|
+
* cast limit query to object like
|
|
76
|
+
* limit=10
|
|
77
|
+
* =>
|
|
78
|
+
* {limit: 10}
|
|
79
|
+
* @param limit
|
|
80
|
+
*/
|
|
81
|
+
private castLimit;
|
|
82
|
+
/**
|
|
83
|
+
* cast lean option like
|
|
84
|
+
* lean=true
|
|
85
|
+
* =>
|
|
86
|
+
* {lean: true}
|
|
87
|
+
* @param lean
|
|
88
|
+
*/
|
|
89
|
+
private castLean;
|
|
90
|
+
/**
|
|
91
|
+
* transform predefined query strings defined in query string to the actual query object out of the given context
|
|
92
|
+
* @param query
|
|
93
|
+
* @param context
|
|
94
|
+
*/
|
|
95
|
+
private parsePredefinedQuery;
|
|
96
|
+
}
|