Joining on a collection's own key falls back to a full scan unless an explicit index on that field is created
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 68/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Tranquilo
- Stack tecnológico
- typescript
- Área
- databases, performance
Línea de trabajo
Comienza ejecutando el repro.mjs proporcionado con y sin INDEX=1 y, después, sigue el planificador de joins utilizado por createLiveQueryCollection y el predicado eq(p.userId, u.id). La corrección está terminada cuando un join de igualdad dirigido al campo getKey de la colección unida utiliza el mapa de claves existente, evita un escaneo completo y no emite ninguna advertencia de índice sin requerir createIndex.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
- Validated against
@tanstack/[email protected](Node 24.18.0)
Summary
A collection is a keyed map — getKey is mandatory, collection.get(key) is O(1), collection.state is a Map<TKey, T>. But the join planner does not recognise the key as an index. Joining on the key field falls back to Falling back to loading all data, and the mount cost becomes linear in the size of the joined collection unless you manually createIndex((row) => row.id) — an index over the exact field the collection is already keyed by.
This is the FK → PK join, i.e. the most common join shape in any normalized schema, so the fallback is easy to hit and the fix is a redundant index.
Measurements
10 posts inner-joined to N users on users.id, timing preload() of the live query (repro below):
| users | no explicit index | users.createIndex(r => r.id) |
|---|---|---|
| 25,000 | 39.4 ms | 4.3 ms |
| 50,000 | 64.4 ms | — |
| 100,000 | 115.5 ms | — |
| 200,000 | 243.4 ms | 4.0 ms |
Unindexed is linear in collection size; indexed is flat. The query produces 10 rows either way.
The warning does fire and names the field correctly:
[TanStack DB] [users] Join requires an index on "id" for efficient loading. Falling back to loading
all data. Consider creating an index on the collection with collection.createIndex((row) => row.id)
So the planner knows it wants an index on id — it just doesn't know the collection already has one, by construction.
Reproduction
// node repro.mjs -> WITHOUT explicit index on users.id
// INDEX=1 node repro.mjs -> WITH
import {
BTreeIndex, createCollection, createLiveQueryCollection, eq, localOnlyCollectionOptions,
} from '@tanstack/db'
const USERS = Number(process.env.USERS ?? 50_000)
const users = createCollection(localOnlyCollectionOptions({
id: 'users',
getKey: (row) => row.id,
initialData: Array.from({ length: USERS }, (_, i) => ({ id: `u${i}`, name: `name-${i}` })),
}))
const posts = createCollection(localOnlyCollectionOptions({
id: 'posts',
getKey: (row) => row.id,
initialData: Array.from({ length: 10 }, (_, i) => ({ id: `p${i}`, userId: `u${i}` })),
}))
await users.preload()
await posts.preload()
// The collection is ALREADY a keyed map on this exact field:
console.log(users.get('u42')) // O(1), no index declared
if (process.env.INDEX === '1') {
users.createIndex((row) => row.id, { indexType: BTreeIndex })
}
const started = performance.now()
const q = createLiveQueryCollection((qb) =>
qb
.from({ p: posts })
.join({ u: users }, ({ p, u }) => eq(p.userId, u.id), 'inner')
.select(({ p, u }) => ({ id: p.id, name: u.name })),
)
await q.preload()
console.log(`${(performance.now() - started).toFixed(1)}ms, ${q.toArray.length} rows, ${USERS} users`)
Expected
An equality join whose predicate targets the joined collection's own key field should use the existing key map rather than a full scan — no user-declared index required, and no warning.
Notes
- Same behaviour for a join nested inside a correlated
select()subquery (an "include"), where it is more costly because the include is instantiated per parent row. autoIndex: 'eager'+defaultIndexTypeis a workaround, but it opts the collection into auto-indexing every queried field, which is a much broader change than "use the key you already have".- Related but distinct: #1700 (size-based suggestion gated on
autoIndex) and #1494 (join warning naming the wrong collection) are both about warning quality. This one is about the optimisation itself — here the warning is correct and the fallback is real.
- Lenguaje dominante
- TypeScript
- Estrellas
- 3.9k
- Forks
- 272
- Merge medio
- 1 d 1 h
- PR fusionados (30 d)
- 212
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de TanStack/db
-
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
electric-db-collection: on-demand + persistence fails after relaunch with "Snapshot requests are not supported in full mode"Posiblemente ocupada @KyleAMathews la tomó hace 3 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 68/100
TanStack/db#2056 · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
TanStack/db#1972 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
Todos los issues de TanStack/db
Issues similares
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 72/100
yjh051108/dsh-routing-suite#216 ·
-
kind/bug priority/needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dependencies view: `getParent` loops forever on untitled documents, extension host runs out of memoryPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 1 día
-
enhancement good first issue
Dificultad 2/5 Medio día Aptitud para principiantes 66/100
apache/fineract-consumer-facing#175 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 Menos de una hora Aptitud para principiantes 82/100
awslabs/visual-asset-management-system#413 ·
Los mantenedores suelen responder en 1 día