Skip to content

Commit d77d82a

Browse files
committed
Merge branch 'develop' of https://github.com/sourabhossain/sequelize-query-parser into develop
2 parents 41d1d02 + 0c3cd0c commit d77d82a

File tree

2 files changed

+120
-99
lines changed

2 files changed

+120
-99
lines changed

.prettierrc.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"semi": true,
3+
"singleQuote": true,
4+
"arrowParens": "always",
5+
"printWidth": 160,
6+
"tabWidth": 4,
7+
"endOfLine": "auto",
8+
"trailingComma": "none"
9+
}

sequelizeQueryParser.js

Lines changed: 111 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
* Author: Twaha Mukammel (t.mukammel@gmail.com)
55
*/
66

7-
'use strict'
7+
'use strict';
88

99
const Promise = require('bluebird');
1010

1111
/**
1212
* Pass `db` connection object which has `Sequelize.Op`
13-
* @param {*} db
13+
* @param {*} db
1414
* @returns object {parse}
1515
*/
1616
module.exports = (db) => {
@@ -24,103 +24,109 @@ module.exports = (db) => {
2424
ne: Op.ne,
2525
eq: Op.eq,
2626
not: Op.not,
27-
like: Op.like, // LIKE '%hat'
28-
notLike: Op.notLike, // NOT LIKE '%hat'
27+
like: Op.like, // LIKE '%hat'
28+
notLike: Op.notLike, // NOT LIKE '%hat'
2929
// iLike: Op.iLike, // ILIKE '%hat' (case insensitive) (PG only)
3030
// notILike: Op.notILike, // NOT ILIKE '%hat' (PG only)
31-
regexp: Op.regexp, // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
32-
notRegexp: Op.notRegexp, // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
31+
regexp: Op.regexp, // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
32+
notRegexp: Op.notRegexp, // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
3333
// iRegexp: Op.iRegexp, // ~* '^[h|a|t]' (PG only)
3434
// notIRegexp: Op.notIRegexp // !~* '^[h|a|t]' (PG only)
35-
and: Op.and, // AND (a = 5)
36-
or: Op.or, // (a = 5 OR a = 6)
37-
between: Op.between, // BETWEEN 6 AND 10
38-
notBetween: Op.notBetween, // NOT BETWEEN 11 AND 15
39-
in: Op.in, // IN [1, 2]
40-
notIn: Op.notIn, // NOT IN [1, 2]
35+
and: Op.and, // AND (a = 5)
36+
or: Op.or, // (a = 5 OR a = 6)
37+
between: Op.between, // BETWEEN 6 AND 10
38+
notBetween: Op.notBetween, // NOT BETWEEN 11 AND 15
39+
in: Op.in, // IN [1, 2]
40+
notIn: Op.notIn // NOT IN [1, 2]
4141
// overlap: Op.overlap, // && [1, 2] (PG array overlap operator)
4242
// contains: Op.contains, // @> [1, 2] (PG array contains operator)
4343
// contained: Op.contained, // <@ [1, 2] (PG array contained by operator)
4444
// col: Op.col // = "user"."organization_id", with dialect specific column identifiers, PG in this example
4545
// any: Op.any // ANY ARRAY[2, 3]::INTEGER (PG only)
46-
}
47-
46+
};
47+
4848
/**
49-
* Split '.' or ',' separated strings to array
50-
* @param {JSON} obj
51-
* @param {array} array
49+
* Split '.' or ',' seperated strings to array
50+
* @param {JSON} obj
51+
* @param {array} array
5252
*/
5353
const splitStringAndBuildArray = (obj, array) => {
54-
const elements = obj.split(',');
55-
56-
elements.forEach(element => {
57-
const fields = element.split('.');
54+
let elements = obj.split(',');
55+
56+
if (elements && elements.length > 0) {
57+
elements.forEach((element) => {
58+
var fields = element.split('.');
59+
if (fields && fields.length > 0) {
60+
array.push(fields);
61+
}
62+
});
63+
}
64+
};
5865

59-
if (fields?.length > 0) {
60-
array.push(fields)
61-
}
62-
});
63-
}
64-
6566
/**
6667
* Parse query params
6768
* @param {string|Array} query
6869
* @returns {Array} sequelize formatted DB query array
6970
*/
7071
const parseFields = (query) => {
7172
let array = null;
72-
73+
7374
if (query !== null) {
7475
array = [];
75-
76+
7677
if (Array.isArray(query) == true) {
77-
query.forEach(obj => {
78+
query.forEach((obj) => {
7879
splitStringAndBuildArray(obj, array);
7980
});
8081
} else {
8182
splitStringAndBuildArray(query, array);
8283
}
8384
}
84-
85+
8586
return array;
86-
}
87-
87+
};
88+
8889
/**
8990
* Replaces operator (json object key) with Sequelize operator.
90-
* @param {JSON} json
91-
* @param {string} key
91+
* @param {JSON} json
92+
* @param {string} key
9293
* @param {Sequelize.op} op
9394
*/
9495
const replaceKeyWithOperator = (json, key, op) => {
9596
const value = json[key];
9697
delete json[key];
9798
json[op] = value;
98-
}
99-
99+
};
100+
100101
/**
101102
* Iteratively replace json keys with Sequelize formatted query operators.
102103
* @param {JSON} json next json
103104
*/
104105
const iterativeReplace = (json) => {
105106
Object.keys(json).forEach((key) => {
106107
if (json[key] !== null && typeof json[key] === 'object') {
107-
const op = operators[key];
108-
108+
// console.debug("key: ", key);
109+
let op = operators[key];
110+
// console.debug("operation: ", op);
111+
109112
if (op) {
110113
replaceKeyWithOperator(json, key, op);
111114
iterativeReplace(json[op]);
112115
} else {
116+
// console.debug("next: ", JSON.stringify(json[key], null, 4));
113117
iterativeReplace(json[key]);
114118
}
115-
}
116-
else if (key == 'model' && db[json[key]] != null) {
117-
json.model = db[json[key]];
119+
} else if (key == 'model' && db[json[key]] != null) {
120+
// json['as'] = json[key].replace(/^./, char => char.toLowerCase());// /^\w/
121+
json['model'] = db[json[key]];
118122
} else {
119-
const op = operators[key];
123+
let op = operators[key];
120124
if (op) replaceKeyWithOperator(json, key, op);
121125
}
126+
127+
// console.debug("After Key:", key, " Query fields: ", JSON.stringify(json, null, 4))
122128
});
123-
}
129+
};
124130

125131
/**
126132
* Unescape escaped sequences in string.
@@ -129,152 +135,158 @@ module.exports = (db) => {
129135
*/
130136
const unescapeEscapedQuery = (query) => {
131137
const queryString = query.toString();
132-
return unescape(queryString);
133-
}
134-
138+
const queryStringUnescaped = unescape(queryString);
139+
return queryStringUnescaped;
140+
};
141+
135142
/**
136143
* Parse and build Sequelize format query
137-
* @param {JSON} query
144+
* @param {JSON} query
138145
* @returns {JSON} sequelize formatted DB query params JSON
139146
*/
140147
const parseQueryFields = (query) => {
141148
const json = JSON.parse(unescapeEscapedQuery(query));
142149
iterativeReplace(json);
143150
return json;
144-
}
145-
151+
};
152+
146153
/**
147154
* Parse and build Sequelize format query
148-
* @param {JSON} query
155+
* @param {JSON} query
149156
* @returns {JSON} sequelize formatted DB include params JSON
150157
*/
151158
const parseIncludeFields = (query) => {
152159
const json = JSON.parse(unescapeEscapedQuery(query));
153160
iterativeReplace(json);
154161
return json;
155-
}
156-
162+
};
163+
157164
/**
158165
* Parse single query parameter
159166
* @param {string} query
160167
* @returns {string|JSON} sequelize formatted DB query param
161168
*/
162169
const parseQueryParam = (query) => {
163-
const elements = query.split(/:(.+)/);
164-
165-
if (elements.length > 1) {
166-
const param = {};
170+
let elements = query.split(/:(.+)/);
171+
// console.debug("Query param: ", JSON.stringify(elements, null, 4));
172+
if (elements && elements.length > 1) {
173+
var param = {};
167174
const elementsArray = elements[1].split(',');
168-
169175
if (elementsArray) {
170-
param[operators[elements[0]]] = elementsArray.length > 1 ? elementsArray : elementsArray[0];
176+
if (elementsArray.length > 1) {
177+
param[operators[elements[0]]] = elementsArray;
178+
} else {
179+
param[operators[elements[0]]] = elementsArray[0];
180+
}
181+
// console.debug("Query param: ", param);
171182
return param;
172183
}
173184
}
174185

175186
return elements[0];
176-
}
177-
187+
};
188+
178189
// Max page size limit is set to 200
179190
const pageSizeLimit = 200;
180-
191+
181192
/**
182-
* Sequelize Query Parser is a very simple package that
193+
* Sequelize Query Parser is a very simple package that
183194
* turns your RESTful APIs into a basic set of Graph APIs.
184-
*
195+
*
185196
* Parses - filter, query, sorting, paging, group, relational object queries
186-
*
197+
*
187198
* fields=field01,field02...
188-
*
199+
*
189200
* limit=value&&offset=value
190-
*
201+
*
191202
* sort_by=field01.asc|field02.desc
192-
*
203+
*
193204
* group_by=field01,field02
194-
*
205+
*
195206
* query=JSON
196-
*
207+
*
197208
* include=JSON
198-
*
209+
*
199210
* filedName=unaryOperator:value
200-
*
211+
*
201212
* @param {JSON} req
202213
* @returns {JSON} sequelize formatted DB query
203214
*/
204215
function parse(req) {
205-
console.debug("Request query: ", req.query);
206-
216+
console.debug('Request query: ', req.query);
217+
207218
return new Promise((resolve, reject) => {
208219
try {
209-
let offset = 0;
210-
let limit = pageSizeLimit;
211-
const dbQuery = {
212-
where: {}
220+
var offset = 0,
221+
limit = pageSizeLimit;
222+
var dbQuery = {
223+
where: {},
224+
offset,
225+
limit
213226
};
214-
227+
215228
for (const key in req.query) {
216229
switch (key) {
217230
// Fields
218231
case 'fields':
219232
// split the field names (attributes) and assign to an array
220-
const fields = req.query.fields.split(",");
233+
let fields = req.query.fields.split(',');
221234
// assign fields array to .attributes
222235
if (fields && fields.length > 0) dbQuery.attributes = fields;
223236
break;
224-
237+
225238
// pagination page size
226239
case 'limit':
227240
dbQuery.limit = Math.min(Math.max(parseInt(req.query.limit), 1), pageSizeLimit);
228241
limit = dbQuery.limit;
229242
break;
230-
243+
231244
// pagination page offset
232245
case 'offset':
233246
offset = Math.max(parseInt(req.query.offset), 0);
234247
break;
235-
248+
236249
// Sort by field order
237250
case 'sort_by':
238251
dbQuery.order = parseFields(req.query.sort_by);
239252
break;
240-
253+
241254
// Group by field
242255
// TODO: Check array
243256
case 'group_by':
244257
dbQuery.group = parseFields(req.query.group_by);
245258
break;
246-
259+
247260
// JSON (nested) query
248261
case 'query':
249262
const parsed = parseQueryFields(req.query.query);
250263
dbQuery.where = { ...dbQuery.where, ...parsed };
251264
break;
252-
265+
253266
// include and query on associated tables
254267
case 'include':
255268
dbQuery.include = parseIncludeFields(req.query.include);
256269
break;
257-
270+
258271
// Simple filter query
259272
default:
260273
dbQuery.where[key] = parseQueryParam(req.query[key]);
261274
break;
262275
}
263276
}
264-
277+
265278
dbQuery.offset = offset * limit;
266-
267-
console.debug("Final sequelize query:");
279+
280+
console.debug('Final sequelize query:');
268281
console.debug(JSON.stringify(dbQuery, null, 4));
269-
282+
270283
resolve(dbQuery);
284+
} catch (error) {
285+
console.debug('Error: ', error.message);
286+
reject([{ msg: error.message }]);
271287
}
272-
catch(error) {
273-
console.debug("Error: ", error.message)
274-
reject([{msg: error.message}]);
275-
}
276-
})
288+
});
277289
}
278-
279-
return { parse }
280-
}
290+
291+
return { parse };
292+
};

0 commit comments

Comments
 (0)