Skip to content

Commit 7a73ce4

Browse files
authored
Revert "Docs for includes (#1317)"
This reverts commit 6aa4f8e.
1 parent 6aa4f8e commit 7a73ce4

File tree

2 files changed

+3
-328
lines changed

2 files changed

+3
-328
lines changed

docs/guides/live-queries.md

Lines changed: 3 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ The result types are automatically inferred from your query structure, providing
3838
- [Select Projections](#select)
3939
- [Joins](#joins)
4040
- [Subqueries](#subqueries)
41-
- [Includes](#includes)
4241
- [groupBy and Aggregations](#groupby-and-aggregations)
4342
- [findOne](#findone)
4443
- [Distinct](#distinct)
@@ -748,8 +747,9 @@ A `join` without a `select` will return row objects that are namespaced with the
748747

749748
The result type of a join will take into account the join type, with the optionality of the joined fields being determined by the join type.
750749

751-
> [!TIP]
752-
> If you need hierarchical results instead of flat joined rows (e.g., each project with its nested issues), see [Includes](#includes) below.
750+
> [!NOTE]
751+
> We are working on an `include` system that will enable joins that project to a hierarchical object. For example an `issue` row could have a `comments` property that is an array of `comment` rows.
752+
> See [this issue](https://github.com/TanStack/db/issues/288) for more details.
753753
754754
### Method Signature
755755

@@ -1040,223 +1040,6 @@ const topUsers = createCollection(liveQueryCollectionOptions({
10401040
}))
10411041
```
10421042

1043-
## Includes
1044-
1045-
Includes let you nest subqueries inside `.select()` to produce hierarchical results. Instead of joins that flatten 1:N relationships into repeated rows, each parent row gets a nested collection of its related items.
1046-
1047-
```ts
1048-
import { createLiveQueryCollection, eq } from '@tanstack/db'
1049-
1050-
const projectsWithIssues = createLiveQueryCollection((q) =>
1051-
q.from({ p: projectsCollection }).select(({ p }) => ({
1052-
id: p.id,
1053-
name: p.name,
1054-
issues: q
1055-
.from({ i: issuesCollection })
1056-
.where(({ i }) => eq(i.projectId, p.id))
1057-
.select(({ i }) => ({
1058-
id: i.id,
1059-
title: i.title,
1060-
})),
1061-
})),
1062-
)
1063-
```
1064-
1065-
Each project's `issues` field is a live `Collection` that updates incrementally as the underlying data changes.
1066-
1067-
### Correlation Condition
1068-
1069-
The child query's `.where()` must contain an `eq()` that links a child field to a parent field — this is the **correlation condition**. It tells the system how children relate to parents.
1070-
1071-
```ts
1072-
// The correlation condition: links issues to their parent project
1073-
.where(({ i }) => eq(i.projectId, p.id))
1074-
```
1075-
1076-
The correlation condition can appear as a standalone `.where()`, or inside an `and()`:
1077-
1078-
```ts
1079-
// Also valid — correlation is extracted from inside and()
1080-
.where(({ i }) => and(eq(i.projectId, p.id), eq(i.status, 'open')))
1081-
```
1082-
1083-
The correlation field does not need to be included in the parent's `.select()`.
1084-
1085-
### Additional Filters
1086-
1087-
Child queries support additional `.where()` clauses beyond the correlation condition, including filters that reference parent fields:
1088-
1089-
```ts
1090-
q.from({ p: projectsCollection }).select(({ p }) => ({
1091-
id: p.id,
1092-
name: p.name,
1093-
issues: q
1094-
.from({ i: issuesCollection })
1095-
.where(({ i }) => eq(i.projectId, p.id)) // correlation
1096-
.where(({ i }) => eq(i.createdBy, p.createdBy)) // parent-referencing filter
1097-
.where(({ i }) => eq(i.status, 'open')) // pure child filter
1098-
.select(({ i }) => ({
1099-
id: i.id,
1100-
title: i.title,
1101-
})),
1102-
}))
1103-
```
1104-
1105-
Parent-referencing filters are fully reactive — if a parent's field changes, the child results update automatically.
1106-
1107-
### Ordering and Limiting
1108-
1109-
Child queries support `.orderBy()` and `.limit()`, applied per parent:
1110-
1111-
```ts
1112-
q.from({ p: projectsCollection }).select(({ p }) => ({
1113-
id: p.id,
1114-
name: p.name,
1115-
issues: q
1116-
.from({ i: issuesCollection })
1117-
.where(({ i }) => eq(i.projectId, p.id))
1118-
.orderBy(({ i }) => i.createdAt, 'desc')
1119-
.limit(5)
1120-
.select(({ i }) => ({
1121-
id: i.id,
1122-
title: i.title,
1123-
})),
1124-
}))
1125-
```
1126-
1127-
Each project gets its own top-5 issues, not 5 issues shared across all projects.
1128-
1129-
### toArray
1130-
1131-
By default, each child result is a live `Collection`. If you want a plain array instead, wrap the child query with `toArray()`:
1132-
1133-
```ts
1134-
import { createLiveQueryCollection, eq, toArray } from '@tanstack/db'
1135-
1136-
const projectsWithIssues = createLiveQueryCollection((q) =>
1137-
q.from({ p: projectsCollection }).select(({ p }) => ({
1138-
id: p.id,
1139-
name: p.name,
1140-
issues: toArray(
1141-
q
1142-
.from({ i: issuesCollection })
1143-
.where(({ i }) => eq(i.projectId, p.id))
1144-
.select(({ i }) => ({
1145-
id: i.id,
1146-
title: i.title,
1147-
})),
1148-
),
1149-
})),
1150-
)
1151-
```
1152-
1153-
With `toArray()`, the project row is re-emitted whenever its issues change. Without it, the child `Collection` updates independently.
1154-
1155-
### Aggregates
1156-
1157-
You can use aggregate functions in child queries. Aggregates are computed per parent:
1158-
1159-
```ts
1160-
import { createLiveQueryCollection, eq, count } from '@tanstack/db'
1161-
1162-
const projectsWithCounts = createLiveQueryCollection((q) =>
1163-
q.from({ p: projectsCollection }).select(({ p }) => ({
1164-
id: p.id,
1165-
name: p.name,
1166-
issueCount: q
1167-
.from({ i: issuesCollection })
1168-
.where(({ i }) => eq(i.projectId, p.id))
1169-
.select(({ i }) => ({ total: count(i.id) })),
1170-
})),
1171-
)
1172-
```
1173-
1174-
Each project gets its own count. The count updates reactively as issues are added or removed.
1175-
1176-
### Nested Includes
1177-
1178-
Includes nest arbitrarily. For example, projects can include issues, which include comments:
1179-
1180-
```ts
1181-
const tree = createLiveQueryCollection((q) =>
1182-
q.from({ p: projectsCollection }).select(({ p }) => ({
1183-
id: p.id,
1184-
name: p.name,
1185-
issues: q
1186-
.from({ i: issuesCollection })
1187-
.where(({ i }) => eq(i.projectId, p.id))
1188-
.select(({ i }) => ({
1189-
id: i.id,
1190-
title: i.title,
1191-
comments: q
1192-
.from({ c: commentsCollection })
1193-
.where(({ c }) => eq(c.issueId, i.id))
1194-
.select(({ c }) => ({
1195-
id: c.id,
1196-
body: c.body,
1197-
})),
1198-
})),
1199-
})),
1200-
)
1201-
```
1202-
1203-
Each level updates independently and incrementally — adding a comment to an issue does not re-process other issues or projects.
1204-
1205-
### Using Includes with React
1206-
1207-
When using includes with React, each child `Collection` needs its own `useLiveQuery` subscription to receive reactive updates. Pass the child collection to a subcomponent that calls `useLiveQuery(childCollection)`:
1208-
1209-
```tsx
1210-
import { useLiveQuery } from '@tanstack/react-db'
1211-
import { eq } from '@tanstack/db'
1212-
1213-
function ProjectList() {
1214-
const { data: projects } = useLiveQuery((q) =>
1215-
q.from({ p: projectsCollection }).select(({ p }) => ({
1216-
id: p.id,
1217-
name: p.name,
1218-
issues: q
1219-
.from({ i: issuesCollection })
1220-
.where(({ i }) => eq(i.projectId, p.id))
1221-
.select(({ i }) => ({
1222-
id: i.id,
1223-
title: i.title,
1224-
})),
1225-
})),
1226-
)
1227-
1228-
return (
1229-
<ul>
1230-
{projects.map((project) => (
1231-
<li key={project.id}>
1232-
{project.name}
1233-
{/* Pass the child collection to a subcomponent */}
1234-
<IssueList issuesCollection={project.issues} />
1235-
</li>
1236-
))}
1237-
</ul>
1238-
)
1239-
}
1240-
1241-
function IssueList({ issuesCollection }) {
1242-
// Subscribe to the child collection for reactive updates
1243-
const { data: issues } = useLiveQuery(issuesCollection)
1244-
1245-
return (
1246-
<ul>
1247-
{issues.map((issue) => (
1248-
<li key={issue.id}>{issue.title}</li>
1249-
))}
1250-
</ul>
1251-
)
1252-
}
1253-
```
1254-
1255-
Each `IssueList` component independently subscribes to its project's issues. When an issue is added or removed, only the affected `IssueList` re-renders — the parent `ProjectList` does not.
1256-
1257-
> [!NOTE]
1258-
> You must pass the child collection to a subcomponent and subscribe with `useLiveQuery`. Reading `project.issues` directly in the parent without subscribing will give you the collection object, but the component won't re-render when the child data changes.
1259-
12601043
## groupBy and Aggregations
12611044

12621045
Use `groupBy` to group your data and apply aggregate functions. When you use aggregates in `select` without `groupBy`, the entire result set is treated as a single group.

packages/react-db/tests/useLiveQuery.test.tsx

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,112 +2481,4 @@ describe(`Query Collections`, () => {
24812481
})
24822482
})
24832483
})
2484-
2485-
describe(`includes subqueries`, () => {
2486-
type Project = {
2487-
id: string
2488-
name: string
2489-
}
2490-
2491-
type ProjectIssue = {
2492-
id: string
2493-
title: string
2494-
projectId: string
2495-
}
2496-
2497-
const sampleProjects: Array<Project> = [
2498-
{ id: `p1`, name: `Alpha` },
2499-
{ id: `p2`, name: `Beta` },
2500-
]
2501-
2502-
const sampleProjectIssues: Array<ProjectIssue> = [
2503-
{ id: `i1`, title: `Bug in Alpha`, projectId: `p1` },
2504-
{ id: `i2`, title: `Feature for Alpha`, projectId: `p1` },
2505-
{ id: `i3`, title: `Bug in Beta`, projectId: `p2` },
2506-
]
2507-
2508-
it(`should render includes results and reactively update child collections`, async () => {
2509-
const projectsCollection = createCollection(
2510-
mockSyncCollectionOptions<Project>({
2511-
id: `includes-react-projects`,
2512-
getKey: (p) => p.id,
2513-
initialData: sampleProjects,
2514-
}),
2515-
)
2516-
2517-
const issuesCollection = createCollection(
2518-
mockSyncCollectionOptions<ProjectIssue>({
2519-
id: `includes-react-issues`,
2520-
getKey: (i) => i.id,
2521-
initialData: sampleProjectIssues,
2522-
}),
2523-
)
2524-
2525-
// Parent hook: runs includes query that produces child Collections
2526-
const { result: parentResult } = renderHook(() =>
2527-
useLiveQuery((q) =>
2528-
q.from({ p: projectsCollection }).select(({ p }) => ({
2529-
id: p.id,
2530-
name: p.name,
2531-
issues: q
2532-
.from({ i: issuesCollection })
2533-
.where(({ i }) => eq(i.projectId, p.id))
2534-
.select(({ i }) => ({
2535-
id: i.id,
2536-
title: i.title,
2537-
})),
2538-
})),
2539-
),
2540-
)
2541-
2542-
// Wait for parent to be ready
2543-
await waitFor(() => {
2544-
expect(parentResult.current.data).toHaveLength(2)
2545-
})
2546-
2547-
const alphaProject = parentResult.current.data.find(
2548-
(p: any) => p.id === `p1`,
2549-
)!
2550-
expect(alphaProject.name).toBe(`Alpha`)
2551-
2552-
// Child hook: subscribes to the child Collection from the parent row,
2553-
// simulating a subcomponent using useLiveQuery(project.issues)
2554-
const { result: childResult } = renderHook(() =>
2555-
useLiveQuery((alphaProject as any).issues),
2556-
)
2557-
2558-
await waitFor(() => {
2559-
expect(childResult.current.data).toHaveLength(2)
2560-
})
2561-
2562-
expect(childResult.current.data).toEqual(
2563-
expect.arrayContaining([
2564-
expect.objectContaining({ id: `i1`, title: `Bug in Alpha` }),
2565-
expect.objectContaining({ id: `i2`, title: `Feature for Alpha` }),
2566-
]),
2567-
)
2568-
2569-
// Add a new issue to Alpha — the child hook should reactively update
2570-
act(() => {
2571-
issuesCollection.utils.begin()
2572-
issuesCollection.utils.write({
2573-
type: `insert`,
2574-
value: { id: `i4`, title: `New Alpha issue`, projectId: `p1` },
2575-
})
2576-
issuesCollection.utils.commit()
2577-
})
2578-
2579-
await waitFor(() => {
2580-
expect(childResult.current.data).toHaveLength(3)
2581-
})
2582-
2583-
expect(childResult.current.data).toEqual(
2584-
expect.arrayContaining([
2585-
expect.objectContaining({ id: `i1`, title: `Bug in Alpha` }),
2586-
expect.objectContaining({ id: `i2`, title: `Feature for Alpha` }),
2587-
expect.objectContaining({ id: `i4`, title: `New Alpha issue` }),
2588-
]),
2589-
)
2590-
})
2591-
})
25922484
})

0 commit comments

Comments
 (0)