@@ -64,7 +64,8 @@ export function processJoins(
64
64
optimizableOrderByCollections : Record < string , OrderByOptimizationInfo > ,
65
65
rawQuery : QueryIR ,
66
66
onCompileSubquery : CompileQueryFn ,
67
- aliasToCollectionId : Record < string , string >
67
+ aliasToCollectionId : Record < string , string > ,
68
+ aliasRemapping : Record < string , string >
68
69
) : NamespacedAndKeyedStream {
69
70
let resultPipeline = pipeline
70
71
@@ -85,7 +86,8 @@ export function processJoins(
85
86
optimizableOrderByCollections ,
86
87
rawQuery ,
87
88
onCompileSubquery ,
88
- aliasToCollectionId
89
+ aliasToCollectionId ,
90
+ aliasRemapping
89
91
)
90
92
}
91
93
@@ -111,7 +113,8 @@ function processJoin(
111
113
optimizableOrderByCollections : Record < string , OrderByOptimizationInfo > ,
112
114
rawQuery : QueryIR ,
113
115
onCompileSubquery : CompileQueryFn ,
114
- aliasToCollectionId : Record < string , string >
116
+ aliasToCollectionId : Record < string , string > ,
117
+ aliasRemapping : Record < string , string >
115
118
) : NamespacedAndKeyedStream {
116
119
const isCollectionRef = joinClause . from . type === `collectionRef`
117
120
@@ -131,7 +134,8 @@ function processJoin(
131
134
cache ,
132
135
queryMapping ,
133
136
onCompileSubquery ,
134
- aliasToCollectionId
137
+ aliasToCollectionId ,
138
+ aliasRemapping
135
139
)
136
140
137
141
// Add the joined source to the sources map
@@ -270,23 +274,18 @@ function processJoin(
270
274
const lazyAliasCandidate =
271
275
activeSource === `main` ? joinedSource : mainSource
272
276
273
- // The alias candidate might be a subquery alias without a direct subscription.
274
- // In that case, find an alias from aliasToCollectionId that maps to the lazy collection.
275
- let lazySourceSubscription = subscriptions [ lazyAliasCandidate ]
276
- if ( ! lazySourceSubscription ) {
277
- // Search for any alias that maps to the lazy collection ID
278
- const matchingAlias = Object . entries ( aliasToCollectionId ) . find (
279
- ( [ _alias , collId ] ) => collId === lazySource . id
280
- ) ?. [ 0 ]
281
-
282
- if ( matchingAlias ) {
283
- lazySourceSubscription = subscriptions [ matchingAlias ]
284
- }
285
- }
277
+ // Find the subscription for lazy loading.
278
+ // For subqueries, the outer join alias (e.g., 'activeUser') may differ from the
279
+ // inner alias (e.g., 'user'). Use aliasRemapping to resolve outer → inner alias.
280
+ // Example: .join({ activeUser: subquery }) where subquery uses .from({ user: collection })
281
+ // → aliasRemapping['activeUser'] = 'user'
282
+ const resolvedAlias =
283
+ aliasRemapping [ lazyAliasCandidate ] || lazyAliasCandidate
284
+ const lazySourceSubscription = subscriptions [ resolvedAlias ]
286
285
287
286
if ( ! lazySourceSubscription ) {
288
287
throw new Error (
289
- `Internal error: subscription for alias '${ lazyAliasCandidate } ' (collection '${ lazySource . id } ') is missing in join pipeline. Available aliases: ${ Object . keys ( subscriptions ) . join ( `, ` ) } . This indicates a bug in alias tracking.`
288
+ `Internal error: subscription for alias '${ resolvedAlias } ' (remapped from ' ${ lazyAliasCandidate } ', collection '${ lazySource . id } ') is missing in join pipeline. Available aliases: ${ Object . keys ( subscriptions ) . join ( `, ` ) } . This indicates a bug in alias tracking.`
290
289
)
291
290
}
292
291
@@ -427,7 +426,8 @@ function processJoinSource(
427
426
cache : QueryCache ,
428
427
queryMapping : QueryMapping ,
429
428
onCompileSubquery : CompileQueryFn ,
430
- aliasToCollectionId : Record < string , string >
429
+ aliasToCollectionId : Record < string , string > ,
430
+ aliasRemapping : Record < string , string >
431
431
) : { alias : string ; input : KeyedStream ; collectionId : string } {
432
432
switch ( from . type ) {
433
433
case `collectionRef` : {
@@ -459,9 +459,20 @@ function processJoinSource(
459
459
queryMapping
460
460
)
461
461
462
- // Pull the nested alias map up so the caller can subscribe to those aliases
463
- // and keep the current alias pointing at the subquery's collection.
462
+ // Pull up the inner alias mappings
464
463
Object . assign ( aliasToCollectionId , subQueryResult . aliasToCollectionId )
464
+ Object . assign ( aliasRemapping , subQueryResult . aliasRemapping )
465
+
466
+ // For subqueries, the outer alias (from.alias) may differ from inner aliases.
467
+ // Find the inner alias that corresponds to the subquery's main collection and create a remapping.
468
+ const innerAlias = Object . keys ( subQueryResult . aliasToCollectionId ) . find (
469
+ ( alias ) =>
470
+ subQueryResult . aliasToCollectionId [ alias ] ===
471
+ subQueryResult . collectionId
472
+ )
473
+ if ( innerAlias && innerAlias !== from . alias ) {
474
+ aliasRemapping [ from . alias ] = innerAlias
475
+ }
465
476
466
477
// Extract the pipeline from the compilation result
467
478
const subQueryInput = subQueryResult . pipeline
0 commit comments