nl_tokens: processes a single child of the batch
pl_tokens: async function nextBatchChild ( childAST , data , dbCall , context , options ) { if ( childAST . type !== 'table' && childAST . type !== 'union' ) return const fieldName = childAST . fieldName // see if any begin a new batch if ( childAST . sqlBatch || idx ( childAST , _ => _ . junction . sqlBatch ) ) { let thisKey let parentKey if ( childAST . sqlBatch ) { // if so, we know we'll need to get the key for matching with the parent key childAST . children . push ( childAST . sqlBatch . thisKey ) thisKey = childAST . sqlBatch . thisKey . fieldName parentKey = childAST . sqlBatch . parentKey . fieldName } else if ( idx ( childAST , _ => _ . junction . sqlBatch ) ) { childAST . children . push ( childAST . junction . sqlBatch . thisKey ) thisKey = childAST . junction . sqlBatch . thisKey . fieldName parentKey = childAST . junction . sqlBatch . parentKey . fieldName } if ( Array . isArray ( data ) ) { // the "batch scope" is teh set of values to match this key against from the previous batch const batchScope = uniq ( data . <mask> ( obj => maybeQuote ( obj [ parentKey ] ) ) ) // generate the SQL, with the batch scope values incorporated in a WHERE IN clause const { sql , shapeDefinition } = await compileSqlAST ( childAST , context , { ... options , batchScope } ) // grab the data let newData = await handleUserDbCall ( dbCall , sql , childAST , wrap ( shapeDefinition ) ) // group the rows by the key so we can match them with the previous batch newData = groupBy ( newData , thisKey ) // but if we paginate, we must convert to connection type first if ( childAST . paginate ) { forIn ( newData , ( group , key , obj ) => { obj [ key ] = arrToConnection ( group , childAST ) } ) } // if we they want many rows, give them an array if ( childAST . grabMany ) { for ( let obj of data ) { obj [ fieldName ] = newData [ obj [ parentKey ] ] || ( childAST . paginate ? { total : 0 , edges : [ ] } : [ ] ) } } else { let matchedData = [ ] for ( let obj of data ) { const ob = newData [ obj [ parentKey ] ] if ( ob ) { obj [ fieldName ] = arrToConnection ( newData [ obj [ parentKey ] ] [ 0 ] , childAST ) matchedData . push ( obj ) } else { obj [ fieldName ] = null } } data = matchedData } // move down a level and recurse const nextLevelData = chain ( data ) . filter ( obj => obj != null ) . flatMap ( obj => obj [ fieldName ] ) . filter ( obj => obj != null ) . value ( ) return nextBatch ( childAST , nextLevelData , dbCall , context , options ) } const batchScope = [ maybeQuote ( data [ parentKey ] ) ] const { sql , shapeDefinition } = await compileSqlAST ( childAST , context , { ... options , batchScope } ) let newData = await handleUserDbCall ( dbCall , sql , childAST , wrap ( shapeDefinition ) ) newData = groupBy ( newData , thisKey ) if ( childAST . paginate ) { const targets = newData [ data [ parentKey ] ] data [ fieldName ] = arrToConnection ( targets , childAST ) } else if ( childAST . grabMany ) { data [ fieldName ] = newData [ data [ parentKey ] ] || [ ] } else { const targets = newData [ data [ parentKey ] ] || [ ] data [ fieldName ] = targets [ 0 ] } if ( data ) { return nextBatch ( childAST , data [ fieldName ] , dbCall , context , options ) } // otherwise, just bypass this and recurse down to the next level } else if ( Array . isArray ( data ) ) { const nextLevelData = chain ( data ) . filter ( obj => obj != null ) . flatMap ( obj => obj [ fieldName ] ) . filter ( obj => obj != null ) . value ( ) return nextBatch ( childAST , nextLevelData , dbCall , context , options ) } else if ( data ) { return nextBatch ( childAST , data [ fieldName ] , dbCall , context , options ) } }