44 * Author: Twaha Mukammel (t.mukammel@gmail.com)
55 */
66
7- 'use strict'
7+ 'use strict' ;
88
99const 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 */
1616module . 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