{"version":3,"file":"joins.cjs","sources":["../../../../src/query/compiler/joins.ts"],"sourcesContent":["import {\n  filter,\n  join as joinOperator,\n  map,\n  serializeValue,\n  tap,\n} from '@tanstack/db-ivm'\nimport {\n  CollectionInputNotFoundError,\n  InvalidJoinCondition,\n  InvalidJoinConditionLeftSourceError,\n  InvalidJoinConditionRightSourceError,\n  InvalidJoinConditionSameSourceError,\n  InvalidJoinConditionSourceMismatchError,\n  JoinCollectionNotFoundError,\n  UnsupportedJoinSourceTypeError,\n  UnsupportedJoinTypeError,\n} from '../../errors.js'\nimport { normalizeValue } from '../../utils/comparison.js'\nimport { ensureIndexForField } from '../../indexes/auto-index.js'\nimport { compileExpression } from './evaluators.js'\nimport { getLazyLoadTargets } from './lazy-targets.js'\nimport type { CompileQueryFn } from './index.js'\nimport type { OrderByOptimizationInfo } from './order-by.js'\nimport type {\n  BasicExpression,\n  CollectionRef,\n  JoinClause,\n  QueryIR,\n  QueryRef,\n} from '../ir.js'\nimport type { IStreamBuilder, JoinType } from '@tanstack/db-ivm'\nimport type { Collection } from '../../collection/index.js'\nimport type {\n  KeyedStream,\n  NamespacedAndKeyedStream,\n  NamespacedRow,\n} from '../../types.js'\nimport type { QueryCache, QueryMapping, WindowOptions } from './types.js'\nimport type { CollectionSubscription } from '../../collection/subscription.js'\n\nexport type LazyDemandPlan = {\n  id: string\n  path: Array<string>\n  collectionId: string\n  initialKeys: Set<unknown>\n}\n\n/** Callbacks for managing lazy-loaded collections in optimized joins */\nexport type LazyCollectionCallbacks = {\n  plans?: Array<LazyDemandPlan>\n  setDemand?: (plan: LazyDemandPlan, keys: Set<unknown>) => void\n}\n\nlet nextLazyDemandPlanId = 0\n\nexport function registerLazyDemandPlan(\n  callbacks: Record<string, LazyCollectionCallbacks>,\n  target: { sourceId: string; path: Array<string>; collection: Collection },\n  initialKeys: Set<unknown> = new Set(),\n): LazyDemandPlan {\n  const plan: LazyDemandPlan = {\n    id: `lazy-demand-${++nextLazyDemandPlanId}`,\n    path: target.path,\n    collectionId: target.collection.id,\n    initialKeys: new Set(initialKeys),\n  }\n  const state = (callbacks[target.sourceId] ??= {})\n  ;(state.plans ??= []).push(plan)\n  return plan\n}\n\n/**\n * Processes all join clauses, applying lazy loading optimizations and maintaining\n * alias tracking for per-alias subscriptions (enables self-joins).\n */\nexport function processJoins(\n  pipeline: NamespacedAndKeyedStream,\n  joinClauses: Array<JoinClause>,\n  sources: Record<string, KeyedStream>,\n  mainCollectionId: string,\n  mainSource: string,\n  allInputs: Record<string, KeyedStream>,\n  cache: QueryCache,\n  queryMapping: QueryMapping,\n  collections: Record<string, Collection>,\n  subscriptions: Record<string, CollectionSubscription>,\n  callbacks: Record<string, LazyCollectionCallbacks>,\n  lazySources: Set<string>,\n  optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n  setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n  rawQuery: QueryIR,\n  onCompileSubquery: CompileQueryFn,\n  aliasToCollectionId: Record<string, string>,\n  aliasRemapping: Record<string, string>,\n  sourceWhereClauses: Map<string, BasicExpression<boolean>>,\n  mainSourceIsParentFiltered: boolean,\n): NamespacedAndKeyedStream {\n  let resultPipeline = pipeline\n\n  for (const joinClause of joinClauses) {\n    resultPipeline = processJoin(\n      resultPipeline,\n      joinClause,\n      sources,\n      mainCollectionId,\n      mainSource,\n      allInputs,\n      cache,\n      queryMapping,\n      collections,\n      subscriptions,\n      callbacks,\n      lazySources,\n      optimizableOrderByCollections,\n      setWindowFn,\n      rawQuery,\n      onCompileSubquery,\n      aliasToCollectionId,\n      aliasRemapping,\n      sourceWhereClauses,\n      mainSourceIsParentFiltered,\n    )\n  }\n\n  return resultPipeline\n}\n\n/**\n * Processes a single join clause with lazy loading optimization.\n * For LEFT/RIGHT/INNER joins, marks one side as \"lazy\" (loads on-demand based on join keys).\n */\nfunction processJoin(\n  pipeline: NamespacedAndKeyedStream,\n  joinClause: JoinClause,\n  sources: Record<string, KeyedStream>,\n  mainCollectionId: string,\n  mainSource: string,\n  allInputs: Record<string, KeyedStream>,\n  cache: QueryCache,\n  queryMapping: QueryMapping,\n  collections: Record<string, Collection>,\n  subscriptions: Record<string, CollectionSubscription>,\n  callbacks: Record<string, LazyCollectionCallbacks>,\n  lazySources: Set<string>,\n  optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n  setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n  rawQuery: QueryIR,\n  onCompileSubquery: CompileQueryFn,\n  aliasToCollectionId: Record<string, string>,\n  aliasRemapping: Record<string, string>,\n  sourceWhereClauses: Map<string, BasicExpression<boolean>>,\n  mainSourceIsParentFiltered: boolean,\n): NamespacedAndKeyedStream {\n  const isCollectionRef = joinClause.from.type === `collectionRef`\n\n  // Get the joined source alias and input stream\n  const {\n    alias: joinedSource,\n    input: joinedInput,\n    collectionId: joinedCollectionId,\n  } = processJoinSource(\n    joinClause.from,\n    allInputs,\n    collections,\n    subscriptions,\n    callbacks,\n    lazySources,\n    optimizableOrderByCollections,\n    setWindowFn,\n    cache,\n    queryMapping,\n    onCompileSubquery,\n    aliasToCollectionId,\n    aliasRemapping,\n    sourceWhereClauses,\n  )\n\n  // Add the joined source to the sources map\n  sources[joinedSource] = joinedInput\n  if (isCollectionRef) {\n    // Only direct collection references form new alias bindings. Subquery\n    // aliases reuse the mapping returned from the recursive compilation above.\n    aliasToCollectionId[joinedSource] = joinedCollectionId\n  }\n\n  const mainCollection = collections[mainCollectionId]\n  const joinedCollection = collections[joinedCollectionId]\n\n  if (!mainCollection) {\n    throw new JoinCollectionNotFoundError(mainCollectionId)\n  }\n\n  if (!joinedCollection) {\n    throw new JoinCollectionNotFoundError(joinedCollectionId)\n  }\n\n  const { activeSource, lazySource } = getActiveAndLazySources(\n    joinClause.type,\n    mainCollection,\n    joinedCollection,\n    mainSourceIsParentFiltered,\n  )\n\n  // Analyze which source each expression refers to and swap if necessary\n  const availableSources = Object.keys(sources)\n  const { mainExpr, joinedExpr } = analyzeJoinExpressions(\n    joinClause.left,\n    joinClause.right,\n    availableSources,\n    joinedSource,\n    rawQuery.from.type === `unionAll`,\n  )\n\n  // Pre-compile the join expressions\n  const compiledMainExpr = compileExpression(mainExpr)\n  const compiledJoinedExpr = compileExpression(joinedExpr)\n\n  // Prepare the main pipeline for joining\n  let mainPipeline = pipeline.pipe(\n    map(([currentKey, namespacedRow]) => {\n      // Extract the join key from the main source expression\n      const mainKey = normalizeValue(compiledMainExpr(namespacedRow))\n\n      // Return [joinKey, [originalKey, namespacedRow]]\n      return [mainKey, [currentKey, namespacedRow]] as [\n        unknown,\n        [string, typeof namespacedRow],\n      ]\n    }),\n  )\n\n  // Prepare the joined pipeline\n  let joinedPipeline = joinedInput.pipe(\n    map(([currentKey, row]) => {\n      // Wrap the row in a namespaced structure\n      const namespacedRow: NamespacedRow = { [joinedSource]: row }\n\n      // Extract the join key from the joined source expression\n      const joinedKey = normalizeValue(compiledJoinedExpr(namespacedRow))\n\n      // Return [joinKey, [originalKey, namespacedRow]]\n      return [joinedKey, [currentKey, namespacedRow]] as [\n        unknown,\n        [string, typeof namespacedRow],\n      ]\n    }),\n  )\n\n  // Apply the join operation\n  if (![`inner`, `left`, `right`, `full`].includes(joinClause.type)) {\n    throw new UnsupportedJoinTypeError(joinClause.type)\n  }\n\n  if (activeSource) {\n    // If the lazy collection comes from a subquery that has a limit and/or an offset clause\n    // then we need to deoptimize the join because we don't know which rows are in the result set\n    // since we simply lookup matching keys in the index but the index contains all rows\n    // (not just the ones that pass the limit and offset clauses)\n    const lazyFrom = activeSource === `main` ? joinClause.from : rawQuery.from\n    const limitedSubquery =\n      lazyFrom.type === `queryRef` &&\n      (lazyFrom.query.limit || lazyFrom.query.offset)\n    const resultUnionLazySide = lazyFrom.type === `unionAll`\n\n    const lazySourceJoinExpr = activeSource === `main` ? joinedExpr : mainExpr\n    const lazyAlias = activeSource === `main` ? joinedSource : mainSource\n    const lazyTargets = resultUnionLazySide\n      ? []\n      : getLazyLoadTargets(\n          rawQuery,\n          lazyFrom,\n          lazyAlias,\n          lazySourceJoinExpr,\n          lazySource,\n          aliasRemapping,\n        )\n\n    if (!limitedSubquery && lazyTargets.length > 0) {\n      // This join can be optimized by having the active collection\n      // dynamically load keys into the lazy collection\n      // based on the value of the joinKey and by looking up\n      // matching rows in the index of the lazy collection\n\n      // Mark the lazy source aliases as lazy\n      // this Set is passed by the liveQueryCollection to the compiler\n      // such that the liveQueryCollection can check it after compilation\n      // to know which source aliases should load data lazily (not initially)\n      for (const target of lazyTargets) {\n        lazySources.add(target.sourceId)\n      }\n\n      const demandPlans = lazyTargets.map((target) =>\n        registerLazyDemandPlan(callbacks, target),\n      )\n      const demandWeights = new Map<string, { key: unknown; weight: number }>()\n\n      const activePipeline =\n        activeSource === `main` ? mainPipeline : joinedPipeline\n\n      for (const target of lazyTargets) {\n        const fieldName = target.path[0]\n        if (fieldName) {\n          ensureIndexForField(fieldName, target.path, target.collection)\n        }\n      }\n\n      // Set up lazy loading: intercept active side's stream and dynamically load\n      // matching rows from lazy side based on join keys.\n      const activePipelineWithLoading: IStreamBuilder<\n        [key: unknown, [originalKey: string, namespacedRow: NamespacedRow]]\n      > = activePipeline.pipe(\n        tap((data) => {\n          for (const [[joinKey], weight] of data.getInner()) {\n            if (joinKey == null) continue\n            const encoded = serializeValue(joinKey)\n            const previous = demandWeights.get(encoded)\n            const nextWeight = (previous?.weight ?? 0) + weight\n            if (nextWeight === 0) {\n              demandWeights.delete(encoded)\n            } else {\n              demandWeights.set(encoded, { key: joinKey, weight: nextWeight })\n            }\n          }\n\n          const keys = new Set(\n            [...demandWeights.values()]\n              .filter(({ weight }) => weight > 0)\n              .map(({ key }) => key),\n          )\n          for (let index = 0; index < lazyTargets.length; index++) {\n            const target = lazyTargets[index]!\n            callbacks[target.sourceId]?.setDemand?.(demandPlans[index]!, keys)\n          }\n        }),\n      )\n\n      if (activeSource === `main`) {\n        mainPipeline = activePipelineWithLoading\n      } else {\n        joinedPipeline = activePipelineWithLoading\n      }\n    }\n  }\n\n  return mainPipeline.pipe(\n    joinOperator(joinedPipeline, joinClause.type as JoinType),\n    processJoinResults(joinClause.type),\n  )\n}\n\n/**\n * Analyzes join expressions to determine which refers to which source\n * and returns them in the correct order (available source expression first, joined source expression second)\n */\nfunction analyzeJoinExpressions(\n  left: BasicExpression,\n  right: BasicExpression,\n  allAvailableSourceAliases: Array<string>,\n  joinedSource: string,\n  allowResultFields: boolean = false,\n): { mainExpr: BasicExpression; joinedExpr: BasicExpression } {\n  // Filter out the joined source alias from the available source aliases\n  const availableSources = allAvailableSourceAliases.filter(\n    (alias) => alias !== joinedSource,\n  )\n\n  const leftSourceAliases = getSourceAliasesFromExpression(left)\n  const rightSourceAliases = getSourceAliasesFromExpression(right)\n  const leftReferencesJoined = leftSourceAliases.has(joinedSource)\n  const rightReferencesJoined = rightSourceAliases.has(joinedSource)\n  const leftAvailableAliases = [...leftSourceAliases].filter(\n    (alias) =>\n      availableSources.includes(alias) ||\n      (allowResultFields && alias !== joinedSource),\n  )\n  const rightAvailableAliases = [...rightSourceAliases].filter(\n    (alias) =>\n      availableSources.includes(alias) ||\n      (allowResultFields && alias !== joinedSource),\n  )\n\n  // If left expression refers to an available source and right refers to joined source, keep as is\n  if (\n    leftAvailableAliases.length > 0 &&\n    !leftReferencesJoined &&\n    rightReferencesJoined &&\n    rightAvailableAliases.length === 0\n  ) {\n    return { mainExpr: left, joinedExpr: right }\n  }\n\n  // If left expression refers to joined source and right refers to an available source, swap them\n  if (\n    leftReferencesJoined &&\n    leftAvailableAliases.length === 0 &&\n    rightAvailableAliases.length > 0 &&\n    !rightReferencesJoined\n  ) {\n    return { mainExpr: right, joinedExpr: left }\n  }\n\n  // If one expression doesn't refer to any source, this is an invalid join\n  if (leftSourceAliases.size === 0 || rightSourceAliases.size === 0) {\n    throw new InvalidJoinConditionSourceMismatchError()\n  }\n\n  // If both expressions refer to the same alias, this is an invalid join\n  if (\n    leftSourceAliases.size === 1 &&\n    rightSourceAliases.size === 1 &&\n    [...leftSourceAliases][0] === [...rightSourceAliases][0]\n  ) {\n    throw new InvalidJoinConditionSameSourceError([...leftSourceAliases][0]!)\n  }\n\n  // Left side must refer to an available source\n  // This cannot happen with the query builder as there is no way to build a ref\n  // to an unavailable source, but just in case, but could happen with the IR\n  if (leftAvailableAliases.length === 0) {\n    throw new InvalidJoinConditionLeftSourceError([...leftSourceAliases][0]!)\n  }\n\n  // Right side must refer to the joined source\n  if (!rightReferencesJoined) {\n    throw new InvalidJoinConditionRightSourceError(joinedSource)\n  }\n\n  // This should not be reachable given the logic above, but just in case\n  throw new InvalidJoinCondition()\n}\n\n/**\n * Extracts the source alias from a join expression\n */\nfunction getSourceAliasesFromExpression(expr: BasicExpression): Set<string> {\n  switch (expr.type) {\n    case `ref`:\n      // PropRef path has the source alias as the first element\n      return new Set(expr.path[0] ? [expr.path[0]] : [])\n    case `func`: {\n      // For function expressions, we need to check if all arguments refer to the same source\n      const sourceAliases = new Set<string>()\n      for (const arg of expr.args) {\n        for (const alias of getSourceAliasesFromExpression(arg)) {\n          sourceAliases.add(alias)\n        }\n      }\n      return sourceAliases\n    }\n    default:\n      // Values (type='val') don't reference any source\n      return new Set()\n  }\n}\n\n/**\n * Processes the join source (collection or sub-query)\n */\nfunction processJoinSource(\n  from: CollectionRef | QueryRef,\n  allInputs: Record<string, KeyedStream>,\n  collections: Record<string, Collection>,\n  subscriptions: Record<string, CollectionSubscription>,\n  callbacks: Record<string, LazyCollectionCallbacks>,\n  lazySources: Set<string>,\n  optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n  setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n  cache: QueryCache,\n  queryMapping: QueryMapping,\n  onCompileSubquery: CompileQueryFn,\n  aliasToCollectionId: Record<string, string>,\n  aliasRemapping: Record<string, string>,\n  sourceWhereClauses: Map<string, BasicExpression<boolean>>,\n): { alias: string; input: KeyedStream; collectionId: string } {\n  switch (from.type) {\n    case `collectionRef`: {\n      const input = allInputs[from.sourceId] ?? allInputs[from.alias]\n      if (!input) {\n        throw new CollectionInputNotFoundError(\n          from.alias,\n          from.collection.id,\n          Object.keys(allInputs),\n        )\n      }\n      aliasToCollectionId[from.alias] = from.collection.id\n      return { alias: from.alias, input, collectionId: from.collection.id }\n    }\n    case `queryRef`: {\n      // Find the original query for caching purposes\n      const originalQuery = queryMapping.get(from.query) || from.query\n\n      // Recursively compile the sub-query with cache\n      const subQueryResult = onCompileSubquery(\n        originalQuery,\n        allInputs,\n        collections,\n        subscriptions,\n        callbacks,\n        lazySources,\n        optimizableOrderByCollections,\n        setWindowFn,\n        cache,\n        queryMapping,\n      )\n\n      // Pull up alias mappings from subquery to parent scope.\n      // This includes both the innermost alias-to-collection mappings AND\n      // any existing remappings from nested subquery levels.\n      Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId)\n      Object.assign(aliasRemapping, subQueryResult.aliasRemapping)\n\n      // Pull up source WHERE clauses from subquery to parent scope.\n      // This enables loadSubset to receive the correct where clauses for subquery collections.\n      //\n      // IMPORTANT: Skip pull-up for optimizer-created subqueries. These are detected when:\n      // 1. The outer alias (from.alias) matches the inner alias (from.query.from.alias)\n      // 2. The subquery was found in queryMapping (it's a user-defined subquery, not optimizer-created)\n      //\n      // For optimizer-created subqueries, the parent already has the sourceWhereClauses\n      // extracted from the original raw query, so pulling up would be redundant.\n      const isUserDefinedSubquery = queryMapping.has(from.query)\n      const fromInnerAlias = getFirstFromAlias(from.query)\n      const isOptimizerCreated =\n        !isUserDefinedSubquery &&\n        fromInnerAlias !== undefined &&\n        from.alias === fromInnerAlias\n\n      if (!isOptimizerCreated) {\n        for (const [alias, whereClause] of subQueryResult.sourceWhereClauses) {\n          sourceWhereClauses.set(alias, whereClause)\n        }\n      }\n\n      // Create a flattened remapping from outer alias to innermost alias.\n      // For nested subqueries, this ensures one-hop lookups (not recursive chains).\n      //\n      // Example with 3-level nesting:\n      //   Inner:  .from({ user: usersCollection })\n      //   Middle: .from({ activeUser: innerSubquery })     → creates: activeUser → user\n      //   Outer:  .join({ author: middleSubquery }, ...)   → creates: author → user (not author → activeUser)\n      //\n      // We search through the PULLED-UP aliasToCollectionId (which contains the\n      // innermost 'user' alias), so we always map directly to the deepest level.\n      // This means aliasRemapping[lazyAlias] is always a single lookup, never recursive.\n      const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(\n        (alias) =>\n          subQueryResult.aliasToCollectionId[alias] ===\n          subQueryResult.collectionId,\n      )\n      if (innerAlias && innerAlias !== from.alias) {\n        aliasRemapping[from.alias] = innerAlias\n      }\n\n      // Extract the pipeline from the compilation result\n      const subQueryInput = subQueryResult.pipeline\n\n      // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n      // We need to extract just the value for use in parent queries\n      const extractedInput = subQueryInput.pipe(\n        map((data: any) => {\n          const [key, [value, _orderByIndex]] = data\n          return [key, value] as [unknown, any]\n        }),\n      )\n\n      return {\n        alias: from.alias,\n        input: extractedInput as KeyedStream,\n        collectionId: subQueryResult.collectionId,\n      }\n    }\n    default:\n      throw new UnsupportedJoinSourceTypeError((from as any).type)\n  }\n}\n\nfunction getFirstFromAlias(query: QueryIR): string | undefined {\n  if (query.from.type === `unionFrom`) {\n    return query.from.sources[0]?.alias\n  }\n\n  if (query.from.type === `unionAll`) {\n    return undefined\n  }\n\n  return query.from.alias\n}\n\n/**\n * Processes the results of a join operation\n */\nfunction processJoinResults(joinType: string) {\n  return function (\n    pipeline: IStreamBuilder<\n      [\n        key: string,\n        [\n          [string, NamespacedRow] | undefined,\n          [string, NamespacedRow] | undefined,\n        ],\n      ]\n    >,\n  ): NamespacedAndKeyedStream {\n    return pipeline.pipe(\n      // Process the join result and handle nulls\n      filter((result) => {\n        const [_key, [main, joined]] = result\n        const mainNamespacedRow = main?.[1]\n        const joinedNamespacedRow = joined?.[1]\n\n        // Handle different join types\n        if (joinType === `inner`) {\n          return !!(mainNamespacedRow && joinedNamespacedRow)\n        }\n\n        if (joinType === `left`) {\n          return !!mainNamespacedRow\n        }\n\n        if (joinType === `right`) {\n          return !!joinedNamespacedRow\n        }\n\n        // For full joins, always include\n        return true\n      }),\n      map((result) => {\n        const [_key, [main, joined]] = result\n        const mainKey = main?.[0]\n        const mainNamespacedRow = main?.[1]\n        const joinedKey = joined?.[0]\n        const joinedNamespacedRow = joined?.[1]\n\n        // Merge the namespaced rows\n        const mergedNamespacedRow: NamespacedRow = {}\n\n        // Add main row data if it exists\n        if (mainNamespacedRow) {\n          Object.assign(mergedNamespacedRow, mainNamespacedRow)\n        }\n\n        // Add joined row data if it exists\n        if (joinedNamespacedRow) {\n          Object.assign(mergedNamespacedRow, joinedNamespacedRow)\n        }\n\n        // We create a composite key that combines the main and joined keys\n        const resultKey = `[${mainKey},${joinedKey}]`\n\n        return [resultKey, mergedNamespacedRow] as [string, NamespacedRow]\n      }),\n    )\n  }\n}\n\n/**\n * Returns the active and lazy collections for a join clause.\n * The active collection is the one that we need to fully iterate over\n * and it can be the main source (i.e. left collection) or the joined source (i.e. right collection).\n * The lazy collection is the one that we should join-in lazily based on matches in the active collection.\n * @param joinClause - The join clause to analyze\n * @param leftCollection - The left collection\n * @param rightCollection - The right collection\n * @returns The active and lazy collections. They are undefined if we need to loop over both collections (i.e. both are active)\n */\nfunction getActiveAndLazySources(\n  joinType: JoinClause[`type`],\n  leftCollection: Collection,\n  rightCollection: Collection,\n  mainSourceIsParentFiltered: boolean,\n):\n  | { activeSource: `main` | `joined`; lazySource: Collection }\n  | { activeSource: undefined; lazySource: undefined } {\n  // Self-joins can now be optimized since we track lazy loading by source alias\n  // rather than collection ID. Each alias has its own subscription and lazy state.\n\n  switch (joinType) {\n    case `left`:\n      return { activeSource: `main`, lazySource: rightCollection }\n    case `right`:\n      return { activeSource: `joined`, lazySource: leftCollection }\n    case `inner`:\n      // A correlated include has already reduced the main relation to active\n      // parent routes. Keep that relation active and load the joined side from\n      // its keys; reversing the join would create an independent demand plan\n      // that widens the correlated source back to a union of constraints.\n      if (mainSourceIsParentFiltered) {\n        return { activeSource: `main`, lazySource: rightCollection }\n      }\n      // The smallest collection should be the active collection\n      // and the biggest collection should be lazy\n      return leftCollection.size < rightCollection.size\n        ? { activeSource: `main`, lazySource: rightCollection }\n        : { activeSource: `joined`, lazySource: leftCollection }\n    default:\n      return { activeSource: undefined, lazySource: undefined }\n  }\n}\n"],"names":["JoinCollectionNotFoundError","compileExpression","map","normalizeValue","UnsupportedJoinTypeError","lazyTargets","getLazyLoadTargets","ensureIndexForField","tap","serializeValue","joinOperator","InvalidJoinConditionSourceMismatchError","InvalidJoinConditionSameSourceError","InvalidJoinConditionLeftSourceError","InvalidJoinConditionRightSourceError","InvalidJoinCondition","CollectionInputNotFoundError","UnsupportedJoinSourceTypeError","filter"],"mappings":"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"}