@@ -74,11 +74,13 @@ export class FirestoreBigQuerySchemaViewFactory {
74
74
datasetId : string ,
75
75
tableNamePrefix : string ,
76
76
schemaName : string ,
77
- firestoreSchema : FirestoreSchema ,
77
+ firestoreSchema : FirestoreSchema
78
78
) : Promise < bigquery . Table > {
79
79
const rawChangeLogTableName = changeLog ( raw ( tableNamePrefix ) ) ;
80
80
const latestRawViewName = latest ( raw ( tableNamePrefix ) ) ;
81
- const changeLogSchemaViewName = changeLog ( schema ( tableNamePrefix , schemaName ) ) ;
81
+ const changeLogSchemaViewName = changeLog (
82
+ schema ( tableNamePrefix , schemaName )
83
+ ) ;
82
84
const latestSchemaViewName = latest ( schema ( tableNamePrefix , schemaName ) ) ;
83
85
const dataset = this . bq . dataset ( datasetId ) ;
84
86
@@ -99,8 +101,16 @@ export class FirestoreBigQuerySchemaViewFactory {
99
101
const [ latestViewExists ] = await latestView . exists ( ) ;
100
102
101
103
if ( ! viewExists ) {
102
- const schemaView = userSchemaView ( datasetId , rawChangeLogTableName , firestoreSchema ) ;
103
- logs . bigQuerySchemaViewCreating ( changeLogSchemaViewName , firestoreSchema , schemaView . query ) ;
104
+ const schemaView = userSchemaView (
105
+ datasetId ,
106
+ rawChangeLogTableName ,
107
+ firestoreSchema
108
+ ) ;
109
+ logs . bigQuerySchemaViewCreating (
110
+ changeLogSchemaViewName ,
111
+ firestoreSchema ,
112
+ schemaView . query
113
+ ) ;
104
114
const options = {
105
115
friendlyName : changeLogSchemaViewName ,
106
116
view : schemaView ,
@@ -110,8 +120,16 @@ export class FirestoreBigQuerySchemaViewFactory {
110
120
}
111
121
112
122
if ( ! latestViewExists ) {
113
- const latestSchemaView = buildSchemaViewQuery ( datasetId , latestRawViewName , firestoreSchema ) ;
114
- logs . bigQuerySchemaViewCreating ( latestSchemaViewName , firestoreSchema , latestSchemaView ) ;
123
+ const latestSchemaView = buildSchemaViewQuery (
124
+ datasetId ,
125
+ latestRawViewName ,
126
+ firestoreSchema
127
+ ) ;
128
+ logs . bigQuerySchemaViewCreating (
129
+ latestSchemaViewName ,
130
+ firestoreSchema ,
131
+ latestSchemaView
132
+ ) ;
115
133
const latestOptions = {
116
134
fiendlyName : latestSchemaViewName ,
117
135
view : latestSchemaView ,
@@ -122,7 +140,6 @@ export class FirestoreBigQuerySchemaViewFactory {
122
140
123
141
return view ;
124
142
}
125
-
126
143
}
127
144
128
145
/**
@@ -132,7 +149,7 @@ export class FirestoreBigQuerySchemaViewFactory {
132
149
export const userSchemaView = (
133
150
datasetId : string ,
134
151
tableName : string ,
135
- schema : FirestoreSchema ,
152
+ schema : FirestoreSchema
136
153
) => ( {
137
154
query : buildSchemaViewQuery ( datasetId , tableName , schema ) ,
138
155
useLegacySql : false ,
@@ -148,14 +165,18 @@ export const buildSchemaViewQuery = (
148
165
rawTableName : string ,
149
166
schema : FirestoreSchema
150
167
) : string => {
151
- const [ fieldExtractors , fieldArrays ] = processFirestoreSchema ( datasetId , "data" , schema ) ;
152
- const fieldValueSelectorClauses = Object . values ( fieldExtractors ) . join ( ', ' ) ;
168
+ const [ fieldExtractors , fieldArrays ] = processFirestoreSchema (
169
+ datasetId ,
170
+ "data" ,
171
+ schema
172
+ ) ;
173
+ const fieldValueSelectorClauses = Object . values ( fieldExtractors ) . join ( ", " ) ;
153
174
const schemaHasArrays = fieldArrays . length > 0 ;
154
175
let query = `
155
176
SELECT
156
177
document_name,
157
178
timestamp,
158
- operation${ fieldValueSelectorClauses . length > 0 ? `,` : `` }
179
+ operation${ fieldValueSelectorClauses . length > 0 ? `,` : `` }
159
180
${ fieldValueSelectorClauses }
160
181
FROM
161
182
\`${ process . env . PROJECT_ID } .${ datasetId } .${ rawTableName } \`
@@ -171,14 +192,18 @@ export const buildSchemaViewQuery = (
171
192
* of additional rows added per document will be the product of the lengths
172
193
* of all the arrays.
173
194
*/
174
- query = `${ subSelectQuery ( query ) } ${ rawTableName } ${ fieldArrays . map ( arrayFieldName =>
175
- `CROSS JOIN UNNEST(${ rawTableName } .${ arrayFieldName } )
195
+ query = `${ subSelectQuery ( query ) } ${ rawTableName } ${ fieldArrays
196
+ . map (
197
+ ( arrayFieldName ) =>
198
+ `CROSS JOIN UNNEST(${ rawTableName } .${ arrayFieldName } )
176
199
AS ${ arrayFieldName } _member
177
- WITH OFFSET ${ arrayFieldName } _index` ) . join ( ' ' ) } `;
200
+ WITH OFFSET ${ arrayFieldName } _index`
201
+ )
202
+ . join ( " " ) } `;
178
203
}
179
204
query = sqlFormatter . format ( query ) ;
180
205
return query ;
181
- }
206
+ } ;
182
207
183
208
/**
184
209
* Given a Cloud Firestore schema which may contain values for any type present
@@ -200,10 +225,19 @@ export function processFirestoreSchema(
200
225
if ( ! transformer ) {
201
226
transformer = ( selector : string ) => selector ;
202
227
}
203
- let extractors : { [ fieldName : string ] : string ; } = { } ;
228
+ let extractors : { [ fieldName : string ] : string } = { } ;
204
229
let arrays : string [ ] = [ ] ;
205
230
let geopoints : string [ ] = [ ] ;
206
- processFirestoreSchemaHelper ( datasetId , dataFieldName , /*prefix=*/ [ ] , schema , arrays , geopoints , extractors , transformer ) ;
231
+ processFirestoreSchemaHelper (
232
+ datasetId ,
233
+ dataFieldName ,
234
+ /*prefix=*/ [ ] ,
235
+ schema ,
236
+ arrays ,
237
+ geopoints ,
238
+ extractors ,
239
+ transformer
240
+ ) ;
207
241
return [ extractors , arrays , geopoints ] ;
208
242
}
209
243
@@ -246,7 +280,13 @@ function processFirestoreSchemaHelper(
246
280
) ;
247
281
return ;
248
282
}
249
- const fieldNameToSelector = ( processLeafField ( datasetId , "data" , prefix , field , transformer ) ) ;
283
+ const fieldNameToSelector = processLeafField (
284
+ datasetId ,
285
+ "data" ,
286
+ prefix ,
287
+ field ,
288
+ transformer
289
+ ) ;
250
290
for ( let fieldName in fieldNameToSelector ) {
251
291
extractors [ fieldName ] = fieldNameToSelector [ fieldName ] ;
252
292
}
@@ -278,46 +318,87 @@ const processLeafField = (
278
318
let fieldNameToSelector = { } ;
279
319
let selector ;
280
320
switch ( field . type ) {
281
- case "null" :
282
- selector = transformer ( `NULL` ) ;
283
- break ;
284
- case "string" :
285
- case "reference" :
286
- selector = jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer ) ;
287
- break ;
288
- case "array" :
289
- selector = firestoreArray ( datasetId , jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer ) ) ;
290
- break ;
291
- case "boolean" :
292
- selector = firestoreBoolean ( datasetId , jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer ) ) ;
293
- break ;
294
- case "number" :
295
- selector = firestoreNumber ( datasetId , jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer ) ) ;
296
- break ;
297
- case "timestamp" :
298
- selector = firestoreTimestamp ( datasetId , jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer ) ) ;
299
- break ;
300
- case "geopoint" :
301
- const latitude = jsonExtract ( dataFieldName , extractPrefix , field , `._latitude` , transformer ) ;
302
- const longitude = jsonExtract ( dataFieldName , extractPrefix , field , `._longitude` , transformer ) ;
303
- /*
304
- * We return directly from this branch because it's the only one that
305
- * generates multiple selector clauses.
306
- */
307
- fieldNameToSelector [ qualifyFieldName ( prefix , field . name ) ] =
308
- `${ firestoreGeopoint (
309
- datasetId ,
310
- jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer )
311
- ) } AS ${ prefix . concat ( field . name ) . join ( "_" ) } `;
312
- fieldNameToSelector [ qualifyFieldName ( prefix , `${ field . name } _latitude` ) ] =
313
- `SAFE_CAST(${ latitude } AS NUMERIC) AS ${ qualifyFieldName ( prefix , `${ field . name } _latitude` ) } ` ;
314
- fieldNameToSelector [ qualifyFieldName ( prefix , `${ field . name } _longitude` ) ] =
315
- `SAFE_CAST(${ longitude } AS NUMERIC) AS ${ qualifyFieldName ( prefix , `${ field . name } _longitude` ) } ` ;
316
- return fieldNameToSelector ;
321
+ case "null" :
322
+ selector = transformer ( `NULL` ) ;
323
+ break ;
324
+ case "string" :
325
+ case "reference" :
326
+ selector = jsonExtract (
327
+ dataFieldName ,
328
+ extractPrefix ,
329
+ field ,
330
+ `` ,
331
+ transformer
332
+ ) ;
333
+ break ;
334
+ case "array" :
335
+ selector = firestoreArray (
336
+ datasetId ,
337
+ jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer )
338
+ ) ;
339
+ break ;
340
+ case "boolean" :
341
+ selector = firestoreBoolean (
342
+ datasetId ,
343
+ jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer )
344
+ ) ;
345
+ break ;
346
+ case "number" :
347
+ selector = firestoreNumber (
348
+ datasetId ,
349
+ jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer )
350
+ ) ;
351
+ break ;
352
+ case "timestamp" :
353
+ selector = firestoreTimestamp (
354
+ datasetId ,
355
+ jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer )
356
+ ) ;
357
+ break ;
358
+ case "geopoint" :
359
+ const latitude = jsonExtract (
360
+ dataFieldName ,
361
+ extractPrefix ,
362
+ field ,
363
+ `._latitude` ,
364
+ transformer
365
+ ) ;
366
+ const longitude = jsonExtract (
367
+ dataFieldName ,
368
+ extractPrefix ,
369
+ field ,
370
+ `._longitude` ,
371
+ transformer
372
+ ) ;
373
+ /*
374
+ * We return directly from this branch because it's the only one that
375
+ * generates multiple selector clauses.
376
+ */
377
+ fieldNameToSelector [
378
+ qualifyFieldName ( prefix , field . name )
379
+ ] = `${ firestoreGeopoint (
380
+ datasetId ,
381
+ jsonExtract ( dataFieldName , extractPrefix , field , `` , transformer )
382
+ ) } AS ${ prefix . concat ( field . name ) . join ( "_" ) } `;
383
+ fieldNameToSelector [
384
+ qualifyFieldName ( prefix , `${ field . name } _latitude` )
385
+ ] = `SAFE_CAST(${ latitude } AS NUMERIC) AS ${ qualifyFieldName (
386
+ prefix ,
387
+ `${ field . name } _latitude`
388
+ ) } `;
389
+ fieldNameToSelector [
390
+ qualifyFieldName ( prefix , `${ field . name } _longitude` )
391
+ ] = `SAFE_CAST(${ longitude } AS NUMERIC) AS ${ qualifyFieldName (
392
+ prefix ,
393
+ `${ field . name } _longitude`
394
+ ) } `;
395
+ return fieldNameToSelector ;
317
396
}
318
- fieldNameToSelector [ qualifyFieldName ( prefix , field . name ) ] = `${ selector } AS ${ qualifyFieldName ( prefix , field . name ) } ` ;
397
+ fieldNameToSelector [
398
+ qualifyFieldName ( prefix , field . name )
399
+ ] = `${ selector } AS ${ qualifyFieldName ( prefix , field . name ) } ` ;
319
400
return fieldNameToSelector ;
320
- }
401
+ } ;
321
402
322
403
/**
323
404
* Extract a field from a raw JSON string that lives in the column
@@ -343,8 +424,12 @@ const jsonExtract = (
343
424
subselector : string = "" ,
344
425
transformer : ( selector : string ) => string
345
426
) => {
346
- return ( transformer ( `JSON_EXTRACT(${ dataFieldName } , \'\$.${ prefix . length > 0 ? `${ prefix } .` : `` } ${ field . name } ${ subselector } \')` ) ) ;
347
- }
427
+ return transformer (
428
+ `JSON_EXTRACT(${ dataFieldName } , \'\$.${
429
+ prefix . length > 0 ? `${ prefix } .` : ``
430
+ } ${ field . name } ${ subselector } \')`
431
+ ) ;
432
+ } ;
348
433
349
434
/**
350
435
* Given a select query, $QUERY, return a query that wraps the result in an
@@ -358,14 +443,24 @@ const jsonExtract = (
358
443
* @param filter an array of field names to filter out from `query`
359
444
*/
360
445
export function subSelectQuery ( query : string , filter ?: string [ ] ) : string {
361
- return ( `SELECT * ${ ( filter && filter . length > 0 ) ? `EXCEPT (${ filter . join ( ', ' ) } )` : `` } FROM (${ query } )` ) ;
446
+ return `SELECT * ${
447
+ filter && filter . length > 0 ? `EXCEPT (${ filter . join ( ", " ) } )` : ``
448
+ } FROM (${ query } )`;
362
449
}
363
450
364
451
function qualifyFieldName ( prefix : string [ ] , name : string ) : string {
365
452
return prefix . concat ( name ) . join ( "_" ) ;
366
- }
453
+ }
367
454
368
- export function latest ( tableName : string ) : string { return `${ tableName } _latest` ; } ;
369
- export function schema ( tableName : string , schemaName : string ) : string { return `${ tableName } _schema_${ schemaName } ` ; } ;
370
- export function raw ( tableName : string ) : string { return `${ tableName } _raw` ; } ;
371
- export function changeLog ( tableName : string ) : string { return `${ tableName } _changelog` ; } ;
455
+ export function latest ( tableName : string ) : string {
456
+ return `${ tableName } _latest` ;
457
+ }
458
+ export function schema ( tableName : string , schemaName : string ) : string {
459
+ return `${ tableName } _schema_${ schemaName } ` ;
460
+ }
461
+ export function raw ( tableName : string ) : string {
462
+ return `${ tableName } _raw` ;
463
+ }
464
+ export function changeLog ( tableName : string ) : string {
465
+ return `${ tableName } _changelog` ;
466
+ }
0 commit comments