@@ -67,6 +67,10 @@ export class SqlCompilerImpl implements SqlCompiler {
6767 projection : ast . columns ? this . convertColumns ( ast . columns ) : undefined ,
6868 } ;
6969
70+ // Check if we need to use aggregate pipeline for column aliases
71+ const hasColumnAliases = ast . columns && Array . isArray ( ast . columns ) &&
72+ ast . columns . some ( ( col : any ) => col . as ) ;
73+
7074 // Handle GROUP BY clause
7175 if ( ast . groupby ) {
7276 command . group = this . convertGroupBy ( ast . groupby , ast . columns ) ;
@@ -86,6 +90,11 @@ export class SqlCompilerImpl implements SqlCompiler {
8690 command . pipeline = this . createAggregatePipeline ( command ) ;
8791 }
8892 }
93+
94+ // If we have column aliases, we need to use aggregate pipeline with $project
95+ if ( hasColumnAliases && ! command . pipeline ) {
96+ command . pipeline = this . createAggregatePipeline ( command ) ;
97+ }
8998
9099 if ( ast . limit ) {
91100 console . log ( 'Limit found in AST:' , JSON . stringify ( ast . limit , null , 2 ) ) ;
@@ -332,7 +341,12 @@ export class SqlCompilerImpl implements SqlCompiler {
332341 */
333342 private convertValue ( value : any ) : any {
334343 if ( typeof value === 'object' ) {
335- if ( 'value' in value ) {
344+ // Handle expression lists (for IN operator)
345+ if ( value . type === 'expr_list' && Array . isArray ( value . value ) ) {
346+ return value . value . map ( ( item : any ) => this . convertValue ( item ) ) ;
347+ }
348+ // Handle single values with value property
349+ else if ( 'value' in value ) {
336350 return value . value ;
337351 }
338352 }
@@ -361,10 +375,12 @@ export class SqlCompilerImpl implements SqlCompiler {
361375 // Handle dot notation (nested fields)
362376 if ( 'column' in column . expr && column . expr . column ) {
363377 const fieldName = this . processFieldName ( column . expr . column ) ;
364- projection [ fieldName ] = 1 ;
378+ const outputField = column . as || fieldName ;
379+ projection [ outputField ] = `$${ fieldName } ` ;
365380 } else if ( column . expr . type === 'column_ref' && column . expr . column ) {
366381 const fieldName = this . processFieldName ( column . expr . column ) ;
367- projection [ fieldName ] = 1 ;
382+ const outputField = column . as || fieldName ;
383+ projection [ outputField ] = `$${ fieldName } ` ;
368384 } else if ( column . expr . type === 'binary_expr' && column . expr . operator === '.' &&
369385 column . expr . left && column . expr . right ) {
370386 // Handle explicit dot notation like table.column
@@ -374,19 +390,22 @@ export class SqlCompilerImpl implements SqlCompiler {
374390 }
375391 if ( fieldName && column . expr . right . column ) {
376392 fieldName += '.' + column . expr . right . column ;
377- projection [ fieldName ] = 1 ;
393+ const outputField = column . as || fieldName ;
394+ projection [ outputField ] = `$${ fieldName } ` ;
378395 }
379396 }
380397 } else if ( 'type' in column && column . type === 'column_ref' && column . column ) {
381398 const fieldName = this . processFieldName ( column . column ) ;
382- projection [ fieldName ] = 1 ;
399+ const outputField = column . as || fieldName ;
400+ projection [ outputField ] = `$${ fieldName } ` ;
383401 } else if ( 'column' in column ) {
384402 const fieldName = this . processFieldName ( column . column ) ;
385- projection [ fieldName ] = 1 ;
403+ const outputField = column . as || fieldName ;
404+ projection [ outputField ] = `$${ fieldName } ` ;
386405 }
387406 } else if ( typeof column === 'string' ) {
388407 const fieldName = this . processFieldName ( column ) ;
389- projection [ fieldName ] = 1 ;
408+ projection [ fieldName ] = `$ ${ fieldName } ` ;
390409 }
391410 } ) ;
392411
@@ -715,12 +734,45 @@ export class SqlCompilerImpl implements SqlCompiler {
715734
716735 // Add $project if projection is specified
717736 if ( command . projection && Object . keys ( command . projection ) . length > 0 ) {
718- pipeline . push ( { $project : command . projection } ) ;
737+ const projectionFormat = this . needsAggregationProjection ( command . projection )
738+ ? this . convertToAggregationProjection ( command . projection )
739+ : command . projection ;
740+ pipeline . push ( { $project : projectionFormat } ) ;
719741 }
720742
721743 console . log ( 'Generated aggregate pipeline:' , JSON . stringify ( pipeline , null , 2 ) ) ;
722744 return pipeline ;
723745 }
746+
747+ /**
748+ * Check if projection needs to be converted to $project format
749+ */
750+ private needsAggregationProjection ( projection : Record < string , any > ) : boolean {
751+ // Check if any value is a string that starts with $
752+ return Object . values ( projection ) . some (
753+ value => typeof value === 'string' && value . startsWith ( '$' )
754+ ) ;
755+ }
756+
757+ /**
758+ * Convert a MongoDB projection to $project format used in aggregation pipeline
759+ */
760+ private convertToAggregationProjection ( projection : Record < string , any > ) : Record < string , any > {
761+ const result : Record < string , any > = { } ;
762+ for ( const [ key , value ] of Object . entries ( projection ) ) {
763+ if ( typeof value === 'string' && value . startsWith ( '$' ) ) {
764+ // This is a field reference, keep it as is
765+ result [ key ] = value ;
766+ } else if ( value === 1 ) {
767+ // For 1 values, convert to field reference
768+ result [ key ] = `$${ key } ` ;
769+ } else {
770+ // Otherwise, keep as is
771+ result [ key ] = value ;
772+ }
773+ }
774+ return result ;
775+ }
724776
725777 /**
726778 * Convert SQL JOINs to MongoDB $lookup stages
0 commit comments