@@ -64,7 +64,8 @@ export function processJoins(
6464 optimizableOrderByCollections : Record < string , OrderByOptimizationInfo > ,
6565 rawQuery : QueryIR ,
6666 onCompileSubquery : CompileQueryFn ,
67- aliasToCollectionId : Record < string , string >
67+ aliasToCollectionId : Record < string , string > ,
68+ aliasRemapping : Record < string , string >
6869) : NamespacedAndKeyedStream {
6970 let resultPipeline = pipeline
7071
@@ -85,7 +86,8 @@ export function processJoins(
8586 optimizableOrderByCollections ,
8687 rawQuery ,
8788 onCompileSubquery ,
88- aliasToCollectionId
89+ aliasToCollectionId ,
90+ aliasRemapping
8991 )
9092 }
9193
@@ -111,7 +113,8 @@ function processJoin(
111113 optimizableOrderByCollections : Record < string , OrderByOptimizationInfo > ,
112114 rawQuery : QueryIR ,
113115 onCompileSubquery : CompileQueryFn ,
114- aliasToCollectionId : Record < string , string >
116+ aliasToCollectionId : Record < string , string > ,
117+ aliasRemapping : Record < string , string >
115118) : NamespacedAndKeyedStream {
116119 const isCollectionRef = joinClause . from . type === `collectionRef`
117120
@@ -131,7 +134,8 @@ function processJoin(
131134 cache ,
132135 queryMapping ,
133136 onCompileSubquery ,
134- aliasToCollectionId
137+ aliasToCollectionId ,
138+ aliasRemapping
135139 )
136140
137141 // Add the joined source to the sources map
@@ -270,23 +274,18 @@ function processJoin(
270274 const lazyAliasCandidate =
271275 activeSource === `main` ? joinedSource : mainSource
272276
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 ]
286285
287286 if ( ! lazySourceSubscription ) {
288287 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.`
290289 )
291290 }
292291
@@ -427,7 +426,8 @@ function processJoinSource(
427426 cache : QueryCache ,
428427 queryMapping : QueryMapping ,
429428 onCompileSubquery : CompileQueryFn ,
430- aliasToCollectionId : Record < string , string >
429+ aliasToCollectionId : Record < string , string > ,
430+ aliasRemapping : Record < string , string >
431431) : { alias : string ; input : KeyedStream ; collectionId : string } {
432432 switch ( from . type ) {
433433 case `collectionRef` : {
@@ -459,9 +459,20 @@ function processJoinSource(
459459 queryMapping
460460 )
461461
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
464463 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+ }
465476
466477 // Extract the pipeline from the compilation result
467478 const subQueryInput = subQueryResult . pipeline
0 commit comments