Skip to content

Commit 8b3086a

Browse files
committed
enable a `new Query.from(...) syntax
1 parent e125e90 commit 8b3086a

19 files changed

+692
-821
lines changed

packages/db/src/query/builder/index.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {
99
OrderBy,
1010
OrderByClause,
1111
OrderByDirection,
12-
Query,
12+
QueryIR,
1313
} from "../ir.js"
1414
import type {
1515
Context,
@@ -27,17 +27,10 @@ import type {
2727
WithResult,
2828
} from "./types.js"
2929

30-
export function buildQuery<TContext extends Context>(
31-
fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>
32-
): Query {
33-
const result = fn(new BaseQueryBuilder())
34-
return getQuery(result)
35-
}
36-
3730
export class BaseQueryBuilder<TContext extends Context = Context> {
38-
private readonly query: Partial<Query> = {}
31+
private readonly query: Partial<QueryIR> = {}
3932

40-
constructor(query: Partial<Query> = {}) {
33+
constructor(query: Partial<QueryIR> = {}) {
4134
this.query = { ...query }
4235
}
4336

@@ -64,7 +57,7 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
6457
ref = new CollectionRef(sourceValue, alias)
6558
} else if (sourceValue instanceof BaseQueryBuilder) {
6659
const subQuery = sourceValue._getQuery()
67-
if (!(subQuery as Partial<Query>).from) {
60+
if (!(subQuery as Partial<QueryIR>).from) {
6861
throw new Error(
6962
`A sub query passed to a ${context} must have a from clause itself`
7063
)
@@ -605,28 +598,44 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
605598
}
606599
}
607600

608-
_getQuery(): Query {
601+
_getQuery(): QueryIR {
609602
if (!this.query.from) {
610603
throw new Error(`Query must have a from clause`)
611604
}
612-
return this.query as Query
605+
return this.query as QueryIR
613606
}
614607
}
615608

616-
export function getQuery(
609+
// Internal function to build a query from a callback
610+
// used by liveQueryCollectionOptions.query
611+
export function buildQuery<TContext extends Context>(
612+
fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>
613+
): QueryIR {
614+
const result = fn(new BaseQueryBuilder())
615+
return getQueryIR(result)
616+
}
617+
618+
// Internal function to get the QueryIR from a builder
619+
export function getQueryIR(
617620
builder: BaseQueryBuilder | QueryBuilder<any> | InitialQueryBuilder
618-
): Query {
621+
): QueryIR {
619622
return (builder as unknown as BaseQueryBuilder)._getQuery()
620623
}
621624

622625
// Type-only exports for the query builder
623626
export type InitialQueryBuilder = Pick<BaseQueryBuilder<Context>, `from`>
624627

628+
export type InitialQueryBuilderConstructor = new () => InitialQueryBuilder
629+
625630
export type QueryBuilder<TContext extends Context> = Omit<
626631
BaseQueryBuilder<TContext>,
627632
`from` | `_getQuery`
628633
>
629634

635+
// Main query builder class alias with the constructor type modified to hide all
636+
// but the from method on the initial instance
637+
export const Query: InitialQueryBuilderConstructor = BaseQueryBuilder
638+
630639
// Helper type to extract context from a QueryBuilder
631640
export type ExtractContext<T> =
632641
T extends BaseQueryBuilder<infer TContext>

packages/db/src/query/compiler/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { processJoins } from "./joins.js"
44
import { processGroupBy } from "./group-by.js"
55
import { processOrderBy } from "./order-by.js"
66
import { processSelectToResults } from "./select.js"
7-
import type { CollectionRef, Query, QueryRef } from "../ir.js"
7+
import type { CollectionRef, QueryIR, QueryRef } from "../ir.js"
88
import type {
99
KeyedStream,
1010
NamespacedAndKeyedStream,
@@ -14,7 +14,7 @@ import type {
1414
/**
1515
* Cache for compiled subqueries to avoid duplicate compilation
1616
*/
17-
type QueryCache = WeakMap<Query, ResultStream>
17+
type QueryCache = WeakMap<QueryIR, ResultStream>
1818

1919
/**
2020
* Compiles a query2 IR into a D2 pipeline
@@ -24,7 +24,7 @@ type QueryCache = WeakMap<Query, ResultStream>
2424
* @returns A stream builder representing the compiled query
2525
*/
2626
export function compileQuery(
27-
query: Query,
27+
query: QueryIR,
2828
inputs: Record<string, KeyedStream>,
2929
cache: QueryCache = new WeakMap()
3030
): ResultStream {

packages/db/src/query/compiler/joins.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
import { compileExpression } from "./evaluators.js"
88
import { compileQuery } from "./index.js"
99
import type { IStreamBuilder, JoinType } from "@electric-sql/d2mini"
10-
import type { CollectionRef, JoinClause, Query, QueryRef } from "../ir.js"
10+
import type { CollectionRef, JoinClause, QueryIR, QueryRef } from "../ir.js"
1111
import type {
1212
KeyedStream,
1313
NamespacedAndKeyedStream,
@@ -18,7 +18,7 @@ import type {
1818
/**
1919
* Cache for compiled subqueries to avoid duplicate compilation
2020
*/
21-
type QueryCache = WeakMap<Query, ResultStream>
21+
type QueryCache = WeakMap<QueryIR, ResultStream>
2222

2323
/**
2424
* Processes all join clauses in a query

packages/db/src/query/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Query builder exports
44
export {
55
BaseQueryBuilder,
6-
buildQuery,
6+
Query,
77
type InitialQueryBuilder,
88
type QueryBuilder,
99
type Context,
@@ -45,7 +45,7 @@ export { val, toExpression, isRefProxy } from "./builder/ref-proxy.js"
4545

4646
// IR types (for advanced usage)
4747
export type {
48-
Query,
48+
QueryIR,
4949
BasicExpression as Expression,
5050
Aggregate,
5151
CollectionRef,

packages/db/src/query/ir.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This is the intermediate representation of the query.
55
import type { CollectionImpl } from "../collection"
66
import type { NamespacedRow } from "../types"
77

8-
export interface Query {
8+
export interface QueryIR {
99
from: From
1010
select?: Select
1111
join?: Join
@@ -77,7 +77,7 @@ export class CollectionRef extends BaseExpression {
7777
export class QueryRef extends BaseExpression {
7878
public type = `queryRef` as const
7979
constructor(
80-
public query: Query,
80+
public query: QueryIR,
8181
public alias: string
8282
) {
8383
super()

packages/db/tests/query/builder/buildQuery.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { CollectionImpl } from "../../../src/collection.js"
33
import { buildQuery } from "../../../src/query/builder/index.js"
44
import { and, eq, gt, or } from "../../../src/query/builder/functions.js"
55

6+
/**
7+
* This is a set of tests for the buildQuery function.
8+
* This function is not used directly by the user, but is used by the
9+
* liveQueryCollectionOptions.query callback or via a useLiveQuery call.
10+
*/
11+
612
// Test schema
713
interface Employee {
814
id: number

0 commit comments

Comments
 (0)