@@ -10,6 +10,7 @@ const parseTypeToPostgresType = type => {
10
10
case 'Object' : return 'jsonb' ;
11
11
case 'Boolean' : return 'boolean' ;
12
12
case 'Pointer' : return 'char(10)' ;
13
+ case 'Number' : return 'double precision' ;
13
14
case 'Array' :
14
15
if ( type . contents && type . contents . type === 'String' ) {
15
16
return 'text[]' ;
@@ -69,8 +70,8 @@ export class PostgresStorageAdapter {
69
70
}
70
71
71
72
addFieldIfNotExists ( className , fieldName , type ) {
72
- // TODO: Doing this in a transaction is probably a good idea.
73
- return this . _client . query ( 'ALTER TABLE $<className:name> ADD COLUMN $<fieldName:name> text ' , { className, fieldName } )
73
+ // TODO: Doing this in a transaction might be a good idea.
74
+ return this . _client . query ( 'ALTER TABLE $<className:name> ADD COLUMN $<fieldName:name> $<postgresType:raw> ' , { className, fieldName, postgresType : parseTypeToPostgresType ( type ) } )
74
75
. catch ( error => {
75
76
if ( error . code === PostgresRelationDoesNotExistError ) {
76
77
return this . createClass ( className , { fields : { [ fieldName ] : type } } )
@@ -161,7 +162,7 @@ export class PostgresStorageAdapter {
161
162
} ) ;
162
163
}
163
164
164
- // TODO: remove the mongo format dependency
165
+ // TODO: remove the mongo format dependency in the return value
165
166
createObject ( className , schema , object ) {
166
167
let columnsArray = [ ] ;
167
168
let valuesArray = [ ] ;
@@ -181,7 +182,7 @@ export class PostgresStorageAdapter {
181
182
} ) ;
182
183
let columnsPattern = columnsArray . map ( ( col , index ) => `$${ index + 2 } :name` ) . join ( ',' ) ;
183
184
let valuesPattern = valuesArray . map ( ( val , index ) => `$${ index + 2 + columnsArray . length } ` ) . join ( ',' ) ;
184
- return this . _client . query ( `INSERT INTO $1~ (${ columnsPattern } ) VALUES (${ valuesPattern } )` , [ className , ...columnsArray , ...valuesArray ] )
185
+ return this . _client . query ( `INSERT INTO $1:name (${ columnsPattern } ) VALUES (${ valuesPattern } )` , [ className , ...columnsArray , ...valuesArray ] )
185
186
. then ( ( ) => ( { ops : [ object ] } ) )
186
187
}
187
188
@@ -204,9 +205,53 @@ export class PostgresStorageAdapter {
204
205
return Promise . reject ( 'Not implented yet.' )
205
206
}
206
207
207
- // Hopefully we can get rid of this in favor of updateObjectsByQuery .
208
+ // Return value not currently well specified .
208
209
findOneAndUpdate ( className , schema , query , update ) {
209
- return Promise . reject ( 'Not implented yet.' )
210
+ let conditionPatterns = [ ] ;
211
+ let updatePatterns = [ ] ;
212
+ let values = [ ]
213
+ values . push ( className ) ;
214
+ let index = 2 ;
215
+
216
+ for ( let fieldName in update ) {
217
+ let fieldValue = update [ fieldName ] ;
218
+ if ( fieldValue . __op === 'Increment' ) {
219
+ updatePatterns . push ( `$${ index } :name = COALESCE($${ index } :name, 0) + $${ index + 1 } ` ) ;
220
+ values . push ( fieldName , fieldValue . amount ) ;
221
+ index += 2 ;
222
+ } else if ( fieldName === 'updatedAt' ) { //TODO: stop special casing this. It should check for __type === 'Date' and use .iso
223
+ updatePatterns . push ( `$${ index } :name = $${ index + 1 } ` )
224
+ values . push ( fieldName , new Date ( fieldValue ) ) ;
225
+ index += 2 ;
226
+ } else {
227
+ return Promise . reject ( new Parse . Error ( Parse . Error . OPERATION_FORBIDDEN , `Postgres doesn't support this type of update yet` ) ) ;
228
+ }
229
+ }
230
+
231
+ for ( let fieldName in query ) {
232
+ let fieldValue = query [ fieldName ] ;
233
+ if ( typeof fieldValue === 'string' ) {
234
+ conditionPatterns . push ( `$${ index } :name = $${ index + 1 } ` ) ;
235
+ values . push ( fieldName , fieldValue ) ;
236
+ index += 2 ;
237
+ } else if ( Array . isArray ( fieldValue . $in ) ) {
238
+ let inPatterns = [ ] ;
239
+ values . push ( fieldName ) ;
240
+ fieldValue . $in . forEach ( ( listElem , listIndex ) => {
241
+ values . push ( listElem ) ;
242
+ inPatterns . push ( `$${ index + 1 + listIndex } ` ) ;
243
+ } ) ;
244
+ conditionPatterns . push ( `$${ index } :name && ARRAY[${ inPatterns . join ( ',' ) } ]` ) ;
245
+ index = index + 1 + inPatterns . length ;
246
+ } else {
247
+ return Promise . reject ( new Parse . Error ( Parse . Error . OPERATION_FORBIDDEN , `Postgres doesn't support this type of request yet` ) ) ;
248
+ }
249
+ }
250
+ let qs = `UPDATE $1:name SET ${ updatePatterns . join ( ',' ) } WHERE ${ conditionPatterns . join ( ' AND ' ) } RETURNING *` ;
251
+ return this . _client . query ( qs , values )
252
+ . then ( val => {
253
+ return val [ 0 ] ;
254
+ } )
210
255
}
211
256
212
257
// Hopefully we can get rid of this. It's only used for config and hooks.
@@ -216,11 +261,61 @@ export class PostgresStorageAdapter {
216
261
217
262
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
218
263
find ( className , schema , query , { skip, limit, sort } ) {
219
- return this . _client . query ( "SELECT * FROM $<className:name>" , { className } )
264
+ let conditionPatterns = [ ] ;
265
+ let values = [ ] ;
266
+ values . push ( className ) ;
267
+ let index = 2 ;
268
+
269
+ for ( let fieldName in query ) {
270
+ let fieldValue = query [ fieldName ] ;
271
+ if ( typeof fieldValue === 'string' ) {
272
+ conditionPatterns . push ( `$${ index } :name = $${ index + 1 } ` ) ;
273
+ values . push ( fieldName , fieldValue ) ;
274
+ index += 2 ;
275
+ } else if ( fieldValue . $ne ) {
276
+ conditionPatterns . push ( `$${ index } :name <> $${ index + 1 } ` ) ;
277
+ values . push ( fieldName , fieldValue . $ne )
278
+ index += 2 ;
279
+ } else if ( Array . isArray ( fieldValue . $in ) ) {
280
+ let inPatterns = [ ] ;
281
+ values . push ( fieldName ) ;
282
+ fieldValue . $in . forEach ( ( listElem , listIndex ) => {
283
+ values . push ( listElem ) ;
284
+ inPatterns . push ( `$${ index + 1 + listIndex } ` ) ;
285
+ } ) ;
286
+ conditionPatterns . push ( `$${ index } :name IN (${ inPatterns . join ( ',' ) } )` ) ;
287
+ index = index + 1 + inPatterns . length ;
288
+ } else if ( fieldValue . __type === 'Pointer' ) {
289
+ conditionPatterns . push ( `$${ index } :name = $${ index + 1 } ` ) ;
290
+ values . push ( fieldName , fieldValue . objectId ) ;
291
+ index += 2 ;
292
+ } else {
293
+ return Promise . reject ( new Parse . Error ( Parse . Error . OPERATION_FORBIDDEN , "Postgres doesn't support this query type yet" ) ) ;
294
+ }
295
+ }
296
+
297
+ return this . _client . query ( `SELECT * FROM $1:name WHERE ${ conditionPatterns . join ( ' AND ' ) } ` , values )
220
298
. then ( results => results . map ( object => {
221
299
Object . keys ( schema . fields ) . filter ( field => schema . fields [ field ] . type === 'Pointer' ) . forEach ( fieldName => {
222
300
object [ fieldName ] = { objectId : object [ fieldName ] , __type : 'Pointer' , className : schema . fields [ fieldName ] . targetClass } ;
223
301
} ) ;
302
+ //TODO: remove this reliance on the mongo format. DB adapter shouldn't know there is a difference between created at and any other date field.
303
+ if ( object . createdAt ) {
304
+ object . createdAt = object . createdAt . toISOString ( ) ;
305
+ }
306
+ if ( object . updatedAt ) {
307
+ object . updatedAt = object . updatedAt . toISOString ( ) ;
308
+ }
309
+ if ( object . expiresAt ) {
310
+ object . expiresAt = { __type : 'Date' , iso : object . expiresAt . toISOString ( ) } ;
311
+ }
312
+
313
+ for ( let fieldName in object ) {
314
+ if ( object [ fieldName ] === null ) {
315
+ delete object [ fieldName ] ;
316
+ }
317
+ }
318
+
224
319
return object ;
225
320
} ) )
226
321
}
0 commit comments