@tanstack/optimistic
Version:
Core optimistic updates library
1 lines • 11.5 kB
Source Map (JSON)
{"version":3,"file":"joins.cjs","sources":["../../../src/query/joins.ts"],"sourcesContent":["import {\n consolidate,\n filter,\n join as joinOperator,\n map,\n} from \"@electric-sql/d2ts\"\nimport { evaluateConditionOnNestedRow } from \"./evaluators.js\"\nimport { extractJoinKey } from \"./extractors.js\"\nimport type { Query } from \"./index.js\"\nimport type { IStreamBuilder, JoinType } from \"@electric-sql/d2ts\"\n\n/**\n * Creates a processing pipeline for join clauses\n */\nexport function processJoinClause(\n pipeline: IStreamBuilder<Record<string, unknown>>,\n query: Query,\n tables: Record<string, IStreamBuilder<Record<string, unknown>>>,\n mainTableAlias: string,\n allInputs: Record<string, IStreamBuilder<Record<string, unknown>>>\n) {\n if (!query.join) return pipeline\n const input = allInputs[query.from]\n\n for (const joinClause of query.join) {\n // Create a stream for the joined table\n const joinedTableAlias = joinClause.as || joinClause.from\n\n // Get the right join type for the operator\n const joinType: JoinType =\n joinClause.type === `cross` ? `inner` : joinClause.type\n\n // We need to prepare the main pipeline and the joined pipeline\n // to have the correct key format for joining\n const mainPipeline = pipeline.pipe(\n map((nestedRow: Record<string, unknown>) => {\n // Extract the key from the ON condition left side for the main table\n const mainRow = nestedRow[mainTableAlias] as Record<string, unknown>\n\n // Extract the join key from the main row\n const keyValue = extractJoinKey(\n mainRow,\n joinClause.on[0],\n mainTableAlias\n )\n\n // Return [key, nestedRow] as a KeyValue type\n return [keyValue, nestedRow] as [unknown, Record<string, unknown>]\n })\n )\n\n // Get the joined table input from the inputs map\n let joinedTableInput: IStreamBuilder<Record<string, unknown>>\n\n if (allInputs[joinClause.from]) {\n // Use the provided input if available\n joinedTableInput = allInputs[joinClause.from]!\n } else {\n // Create a new input if not provided\n joinedTableInput = input!.graph.newInput<Record<string, unknown>>()\n }\n\n tables[joinedTableAlias] = joinedTableInput\n\n // Create a pipeline for the joined table\n const joinedPipeline = joinedTableInput.pipe(\n map((row: Record<string, unknown>) => {\n // Wrap the row in an object with the table alias as the key\n const nestedRow = { [joinedTableAlias]: row }\n\n // Extract the key from the ON condition right side for the joined table\n const keyValue = extractJoinKey(row, joinClause.on[2], joinedTableAlias)\n\n // Return [key, nestedRow] as a KeyValue type\n return [keyValue, nestedRow] as [unknown, Record<string, unknown>]\n })\n )\n\n // Apply join with appropriate typings based on join type\n switch (joinType) {\n case `inner`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `inner`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n case `left`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `left`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n case `right`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `right`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n case `full`:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `full`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n break\n default:\n pipeline = mainPipeline.pipe(\n joinOperator(joinedPipeline, `inner`),\n consolidate(),\n processJoinResults(mainTableAlias, joinedTableAlias, joinClause)\n )\n }\n }\n return pipeline\n}\n\n/**\n * Creates a processing pipeline for join results\n */\nexport function processJoinResults(\n mainTableAlias: string,\n joinedTableAlias: string,\n joinClause: { on: any; where?: any; type: string }\n) {\n return function (\n pipeline: IStreamBuilder<unknown>\n ): IStreamBuilder<Record<string, unknown>> {\n return pipeline.pipe(\n // Process the join result and handle nulls in the same step\n map((result: unknown) => {\n const [_key, [mainNestedRow, joinedNestedRow]] = result as [\n unknown,\n [\n Record<string, unknown> | undefined,\n Record<string, unknown> | undefined,\n ],\n ]\n\n // For inner joins, both sides should be non-null\n if (joinClause.type === `inner` || joinClause.type === `cross`) {\n if (!mainNestedRow || !joinedNestedRow) {\n return undefined // Will be filtered out\n }\n }\n\n // For left joins, the main row must be non-null\n if (joinClause.type === `left` && !mainNestedRow) {\n return undefined // Will be filtered out\n }\n\n // For right joins, the joined row must be non-null\n if (joinClause.type === `right` && !joinedNestedRow) {\n return undefined // Will be filtered out\n }\n\n // Merge the nested rows\n const mergedNestedRow: Record<string, unknown> = {}\n\n // Add main row data if it exists\n if (mainNestedRow) {\n Object.entries(mainNestedRow).forEach(([tableAlias, tableData]) => {\n mergedNestedRow[tableAlias] = tableData\n })\n }\n\n // If we have a joined row, add it to the merged result\n if (joinedNestedRow) {\n Object.entries(joinedNestedRow).forEach(([tableAlias, tableData]) => {\n mergedNestedRow[tableAlias] = tableData\n })\n } else if (joinClause.type === `left` || joinClause.type === `full`) {\n // For left or full joins, add the joined table with null data if missing\n mergedNestedRow[joinedTableAlias] = null\n }\n\n // For right or full joins, add the main table with null data if missing\n if (\n !mainNestedRow &&\n (joinClause.type === `right` || joinClause.type === `full`)\n ) {\n mergedNestedRow[mainTableAlias] = null\n }\n\n return mergedNestedRow\n }),\n // Filter out undefined results\n filter(\n (value: unknown): value is Record<string, unknown> =>\n value !== undefined\n ),\n // Process the ON condition\n filter((nestedRow: Record<string, unknown>) => {\n // If there's no ON condition, or it's a cross join, always return true\n if (!joinClause.on || joinClause.type === `cross`) {\n return true\n }\n\n // For LEFT JOIN, if the right side is null, we should include the row\n if (\n joinClause.type === `left` &&\n nestedRow[joinedTableAlias] === null\n ) {\n return true\n }\n\n // For RIGHT JOIN, if the left side is null, we should include the row\n if (joinClause.type === `right` && nestedRow[mainTableAlias] === null) {\n return true\n }\n\n // For FULL JOIN, if either side is null, we should include the row\n if (\n joinClause.type === `full` &&\n (nestedRow[mainTableAlias] === null ||\n nestedRow[joinedTableAlias] === null)\n ) {\n return true\n }\n\n const result = evaluateConditionOnNestedRow(\n nestedRow,\n joinClause.on,\n mainTableAlias,\n joinedTableAlias\n )\n return result\n }),\n // Process the WHERE clause for the join if it exists\n filter((nestedRow: Record<string, unknown>) => {\n if (!joinClause.where) {\n return true\n }\n\n const result = evaluateConditionOnNestedRow(\n nestedRow,\n joinClause.where,\n mainTableAlias,\n joinedTableAlias\n )\n return result\n })\n )\n }\n}\n"],"names":["map","extractJoinKey","joinOperator","consolidate","filter","evaluateConditionOnNestedRow"],"mappings":"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"}