@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 23.5 kB
Source Map (JSON)
{"version":3,"file":"order-by.cjs","sources":["../../../../src/query/compiler/order-by.ts"],"sourcesContent":["import {\n groupedOrderByWithFractionalIndex,\n orderByWithFractionalIndex,\n} from '@tanstack/db-ivm'\nimport { defaultComparator, makeComparator } from '../../utils/comparison.js'\nimport {\n PropRef,\n collectCollectionSources,\n followRef,\n getWhereExpression,\n isResidualWhere,\n} from '../ir.js'\nimport { ensureIndexForField } from '../../indexes/auto-index.js'\nimport {\n findIndexForField,\n supportsOrderedIndexStructure,\n supportsOrderedRange,\n} from '../../utils/index-optimization.js'\nimport { hasVirtualPropPath } from '../../virtual-props.js'\nimport { devBuild } from '../../error-message.js'\nimport { compileExpression } from './evaluators.js'\nimport { getSourceAliasesFromExpression } from './expressions.js'\nimport { replaceAggregatesByRefs } from './group-by.js'\nimport type { CompareOptions } from '../builder/types.js'\nimport type { WindowOptions } from './types.js'\nimport type { CompiledSingleRowExpression } from './evaluators.js'\nimport type { OrderBy, OrderByClause, QueryIR, Select } from '../ir.js'\nimport type {\n CollectionLike,\n NamespacedAndKeyedStream,\n NamespacedRow,\n} from '../../types.js'\nimport type {\n IStreamBuilder,\n KeyValue,\n SetSizeCallback,\n} from '@tanstack/db-ivm'\nimport type { IndexReader } from '../../indexes/base-index.js'\nimport type { Collection } from '../../collection/index.js'\n\nexport type OrderByOptimizationInfo = {\n sourceId: string\n alias: string\n orderBy: OrderBy\n offset: number\n limit: number\n comparator: (\n a: Record<string, unknown> | null | undefined,\n b: Record<string, unknown> | null | undefined,\n ) => number\n /** Extracts the leading provider order value from a raw row. */\n valueExtractorForRawRow: (row: Record<string, unknown>) => unknown\n /** Index on the first orderBy column - used for lazy loading */\n index?: IndexReader<string | number>\n /**\n * Rows the window still needs. Given the loader's settled boundary, a window\n * row after it does not count: the graph may lack an earlier eligible row.\n */\n dataNeeded?: (boundary?: Record<string, unknown>) => number\n /** Reads the source loader's synchronous request guard, when installed. */\n isRequesting?: () => boolean\n /**\n * Whether a provider-ordered finite prefix is insufficient for the local\n * plan, including when a custom local collation defines another order.\n */\n requiresFullSource: boolean\n /** Whether the ordered source's installed rows are the whole source. */\n sourceHoldsAllRows: boolean\n /** Source whose lazy demand must settle before a joined-filter page. */\n joinedFilterSourceId?: string\n}\n\nfunction warnOrderIndexFallback(\n collection: Collection,\n path: Array<string>,\n options: CompareOptions,\n index?: IndexReader<string | number>,\n): void {\n if (devBuild() && process.env.NODE_ENV !== `production`) {\n const field = path.join(`.`)\n const prefix = `[TanStack DB]${collection.id ? ` [${collection.id}]` : ``} orderBy with limit`\n if (options.stringSort === `custom`) {\n console.warn(\n `${prefix} uses a custom string comparator on \"${field}\". On-demand sources require a filtered full source request for local sorting. An index with the same compare function reference can sort installed rows, but cannot make provider pagination safe.`,\n )\n return\n }\n if (hasVirtualPropPath(path)) {\n console.warn(\n `${prefix} orders virtual field \"${field}\"; Collection indexes cannot cover virtual fields, so creating one will not enable ordered lazy loading.`,\n )\n return\n }\n if (index) return\n\n const fieldIndexes = [...collection.indexes.values()].filter((candidate) =>\n candidate.matchesField(path),\n )\n let detail: string\n if (fieldIndexes.length === 0) {\n detail = `requires an index on \"${field}\" for efficient lazy loading. Consider collection.createIndex((row) => row.${field}) or autoIndex: 'eager' with a defaultIndexType.`\n } else if (\n fieldIndexes.every(\n (candidate) => !candidate.matchesCompareOptions(options),\n )\n ) {\n detail = `found an index on \"${field}\", but none uses the query's compareOptions (nulls: \"${options.nulls}\", stringSort: \"${options.stringSort ?? `locale`}\"${options.stringSort === `locale` ? `, including the same locale and localeOptions` : ``}). After importing BasicIndex from '@tanstack/db', use collection.createIndex((row) => row.${field}, { indexType: BasicIndex, options: { compareOptions: { direction: \"asc\", nulls: \"${options.nulls}\", stringSort: \"${options.stringSort ?? `locale`}\" } } }). Match the query's nulls and string collation, including any locale and localeOptions. The index direction may differ.`\n } else {\n detail = `found an index on \"${field}\" with matching compareOptions, but none supports ordered lazy loading. Use an index with ordered range reads, one comparable value domain, and no custom comparator.`\n }\n console.warn(\n `${prefix} ${detail} Local reads may scan or sort installed rows; an underfilled on-demand window may require a full-source request.`,\n )\n }\n}\n\n/**\n * Processes the ORDER BY clause\n * Works with the new structure that has both namespaced row data and $selected\n * Always uses fractional indexing and adds the index as __ordering_index to the result\n */\nexport function processOrderBy(\n rawQuery: QueryIR,\n pipeline: NamespacedAndKeyedStream,\n orderByClause: Array<OrderByClause>,\n selectClause: Select,\n collection: Collection,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n limit?: number,\n offset?: number,\n groupKeyFn?: (key: unknown, value: unknown) => unknown,\n): IStreamBuilder<KeyValue<unknown, [NamespacedRow, string]>> {\n const compiledOrderBy = orderByClause.map((clause) => {\n // Aggregate references read the SELECT result under $selected.\n const clauseWithoutAggregates = replaceAggregatesByRefs(\n clause.expression,\n selectClause,\n `$selected`,\n )\n\n return {\n compiledExpression: compileExpression(clauseWithoutAggregates),\n compareOptions: buildCompareOptions(clause, collection),\n }\n })\n // The row holds source aliases and any projected aggregate under $selected.\n const valueExtractor = (row: NamespacedRow & { $selected?: any }) => {\n if (orderByClause.length > 1) {\n return compiledOrderBy.map((compiled) => compiled.compiledExpression(row))\n }\n return orderByClause.length === 1\n ? compiledOrderBy[0]!.compiledExpression(row)\n : null\n }\n\n // Compare order terms in declaration order, stopping at the first difference.\n const compare = (a: unknown, b: unknown) => {\n if (orderByClause.length > 1) {\n const arrayA = a as Array<unknown>\n const arrayB = b as Array<unknown>\n for (let i = 0; i < orderByClause.length; i++) {\n const clause = compiledOrderBy[i]!\n const compareFn = makeComparator(clause.compareOptions)\n const result = compareFn(arrayA[i], arrayB[i])\n if (result !== 0) {\n return result\n }\n }\n return arrayA.length - arrayB.length\n }\n\n if (orderByClause.length === 1) {\n const clause = compiledOrderBy[0]!\n const compareFn = makeComparator(clause.compareOptions)\n return compareFn(a, b)\n }\n\n return defaultComparator(a, b)\n }\n\n let setSizeCallback: SetSizeCallback | undefined\n\n let orderByOptimizationInfo: OrderByOptimizationInfo | undefined\n\n // When there's a limit, create orderByOptimizationInfo for top-K source\n // loading. Unbounded queries use subscription hints instead. A plan whose\n // local semantics cannot be established from a provider prefix records\n // requiresFullSource and issues one filtered full-source acquisition.\n // We try to use an index on the FIRST orderBy column for lazy loading,\n // even for multi-column orderBy (using wider bounds on first column).\n // Skip this optimization when using grouped ordering (includes with limit),\n // because the limit is per-group, not global — the child collection needs all data loaded.\n if (\n limit !== undefined &&\n orderByClause.length > 0 &&\n !groupKeyFn &&\n rawQuery.from.type !== `unionFrom` &&\n rawQuery.from.type !== `unionAll`\n ) {\n let index: IndexReader<string | number> | undefined\n let followRefCollection: Collection | undefined\n let orderByAlias: string = rawQuery.from.alias\n let orderBySourceId: string | undefined\n\n // Try to create/find an index on the FIRST orderBy column for lazy loading\n const firstClause = orderByClause[0]!\n const firstOrderByExpression = firstClause.expression\n\n const followRefResult =\n firstOrderByExpression.type === `ref`\n ? followRef(rawQuery, firstOrderByExpression, collection)\n : undefined\n if (firstOrderByExpression.type === `ref` && followRefResult) {\n followRefCollection = followRefResult.collection\n orderBySourceId = followRefResult.sourceId\n const fieldName = followRefResult.path[0]\n // The query's first source defines implicit string collation for the\n // whole order. Build the source index with that same resolved term so\n // provider admission cannot disagree with emitted query order.\n const compareOpts = buildCompareOptions(firstClause, collection)\n\n if (fieldName) {\n ensureIndexForField(\n fieldName,\n followRefResult.path,\n followRefCollection,\n compareOpts,\n supportsOrderedIndexStructure,\n )\n }\n\n index = findIndexForField(\n followRefCollection,\n followRefResult.path,\n compareOpts,\n supportsOrderedRange,\n )\n\n warnOrderIndexFallback(\n followRefCollection,\n followRefResult.path,\n compareOpts,\n index,\n )\n\n orderByAlias =\n firstOrderByExpression.path.length > 1\n ? String(firstOrderByExpression.path[0])\n : rawQuery.from.alias\n orderBySourceId ??= collectCollectionSources(rawQuery).find(\n (source) =>\n source.alias === orderByAlias &&\n source.collection === followRefCollection,\n )?.sourceId\n }\n\n if (orderBySourceId && followRefResult) {\n const sourceOrderBy = resolveOrderBy(\n orderByClause,\n collection.compareOptions,\n )\n const sourceOrderIsDirect = orderByClause.every(({ expression }) => {\n if (expression.type !== `ref`) return false\n return (\n followRef(rawQuery, expression, collection)?.sourceId ===\n orderBySourceId\n )\n })\n const extract = compileExpression(\n new PropRef(followRefResult.path),\n true,\n ) as CompiledSingleRowExpression\n const compareTerm = makeComparator(sourceOrderBy[0]!.compareOptions)\n const compareSourceRows = (\n a: Record<string, unknown> | null | undefined,\n b: Record<string, unknown> | null | undefined,\n ) => compareTerm(a ? extract(a) : a, b ? extract(b) : b)\n const hasCrossAliasWhere =\n rawQuery.where?.some((where) =>\n [...getSourceAliasesFromExpression(getWhereExpression(where))].some(\n (alias) => alias !== orderByAlias,\n ),\n ) ?? false\n const joinedFilterSourceId =\n hasCrossAliasWhere &&\n rawQuery.join?.length === 1 &&\n rawQuery.join[0]!.type === `left` &&\n rawQuery.join[0]!.from.type === `collectionRef`\n ? rawQuery.join[0]!.from.sourceId\n : undefined\n\n const info: OrderByOptimizationInfo = {\n sourceId: orderBySourceId,\n alias: orderByAlias,\n offset: offset ?? 0,\n limit,\n comparator: compareSourceRows,\n valueExtractorForRawRow: extract,\n index,\n orderBy: sourceOrderBy,\n joinedFilterSourceId,\n sourceHoldsAllRows:\n followRefResult.collection.config.syncMode !== `on-demand`,\n requiresFullSource:\n sourceOrderBy.some(\n ({ compareOptions }) => compareOptions.stringSort === `custom`,\n ) ||\n !sourceOrderIsDirect ||\n rawQuery.from.type !== `collectionRef` ||\n rawQuery.from.sourceId !== orderBySourceId ||\n // Only a LEFT join keeps every root row of a bounded prefix and\n // adds no row the prefix cannot place: inner and cross joins drop\n // root rows, and right, full and outer joins add joined rows whose\n // root match may sit past the prefix.\n (rawQuery.join?.some(({ type }) => type !== `left`) ?? false) ||\n (rawQuery.where?.some(isResidualWhere) ?? false) ||\n (hasCrossAliasWhere && joinedFilterSourceId === undefined) ||\n (rawQuery.fnWhere?.length ?? 0) > 0 ||\n rawQuery.groupBy !== undefined ||\n rawQuery.having !== undefined ||\n rawQuery.fnHaving !== undefined ||\n rawQuery.distinct === true,\n }\n orderByOptimizationInfo = info\n\n // Ordered loading is owned by one lexical source. A collection can occur\n // more than once in a query tree, so collection ID and alias are not\n // sufficient identities here.\n optimizableOrderByCollections[orderBySourceId] = info\n\n // loadMoreIfNeeded reads how many rows the window needs. Without an\n // index, only an on-demand joined filter can leave the window short.\n const needsCount =\n info.joinedFilterSourceId !== undefined && !info.sourceHoldsAllRows\n if (index || needsCount) {\n setSizeCallback = (getSize) => {\n info.dataNeeded = (boundary) => {\n // Window rows at or before the boundary are a leading run of\n // the window order.\n const size = getSize(\n boundary &&\n ((row) =>\n compareSourceRows(\n (row as NamespacedRow)[orderByAlias],\n boundary,\n ) <= 0),\n )\n return Math.max(0, info.limit - size)\n }\n }\n }\n }\n }\n\n // Use grouped ordering when a groupKeyFn is provided (includes with limit/offset),\n // otherwise use the standard global ordering operator.\n if (groupKeyFn) {\n return pipeline.pipe(\n groupedOrderByWithFractionalIndex(valueExtractor, {\n limit,\n offset,\n comparator: compare,\n setSizeCallback,\n groupKeyFn,\n setWindowFn: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => {\n setWindowFn((options) => {\n windowFn(options)\n if (orderByOptimizationInfo) {\n orderByOptimizationInfo.offset =\n options.offset ?? orderByOptimizationInfo.offset\n orderByOptimizationInfo.limit =\n options.limit ?? orderByOptimizationInfo.limit\n }\n })\n },\n }),\n )\n }\n\n // Use fractional indexing and return the tuple [value, index]\n return pipeline.pipe(\n orderByWithFractionalIndex(valueExtractor, {\n limit,\n offset,\n comparator: compare,\n setSizeCallback,\n setWindowFn: (\n windowFn: (options: { offset?: number; limit?: number }) => void,\n ) => {\n setWindowFn(\n // We wrap the move function such that we update the orderByOptimizationInfo\n // because that is used by the `dataNeeded` callback to determine if we need to load more data\n (options) => {\n windowFn(options)\n if (orderByOptimizationInfo) {\n orderByOptimizationInfo.offset =\n options.offset ?? orderByOptimizationInfo.offset\n orderByOptimizationInfo.limit =\n options.limit ?? orderByOptimizationInfo.limit\n }\n },\n )\n },\n }),\n // orderByWithFractionalIndex returns [key, [value, index]] - we keep this format\n )\n}\n\n/**\n * Builds a comparison configuration object that uses the values provided in the orderBy clause.\n * If no string sort configuration is provided it defaults to the collection's string sort configuration.\n * Multi-source FROM queries pass their first source collection here as the\n * documented default. Use explicit orderBy compare options when branches need\n * different string collation behavior.\n */\nexport function buildCompareOptions(\n clause: OrderByClause,\n collection: CollectionLike<any, any>,\n): CompareOptions {\n return resolveCompareOptions(clause, collection.compareOptions)\n}\n\nfunction resolveOrderBy(\n orderBy: OrderBy,\n defaults: CollectionLike[`compareOptions`],\n): OrderBy {\n return orderBy.map((clause) => ({\n expression: clause.expression,\n compareOptions: resolveCompareOptions(clause, defaults),\n }))\n}\n\nfunction resolveCompareOptions(\n clause: OrderByClause,\n defaults: CollectionLike[`compareOptions`],\n): CompareOptions {\n return clause.compareOptions.stringSort === undefined\n ? {\n ...defaults,\n direction: clause.compareOptions.direction,\n nulls: clause.compareOptions.nulls,\n }\n : clause.compareOptions\n}\n"],"names":["devBuild","hasVirtualPropPath","replaceAggregatesByRefs","compileExpression","makeComparator","defaultComparator","followRef","ensureIndexForField","supportsOrderedIndexStructure","findIndexForField","supportsOrderedRange","collectCollectionSources","PropRef","getSourceAliasesFromExpression","getWhereExpression","isResidualWhere","groupedOrderByWithFractionalIndex","orderByWithFractionalIndex"],"mappings":"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"}