Skip to content
This repository was archived by the owner on Mar 18, 2025. It is now read-only.

Commit 1e351e4

Browse files
committed
feat: limit, offset and ordering
1 parent a699c65 commit 1e351e4

File tree

9 files changed

+95
-9
lines changed

9 files changed

+95
-9
lines changed

src/classes/clickhouse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class ClickHouse<T extends Record<string, Table>> {
5959
});
6060

6161
const primaryKeyColumn = Object.entries(table.columns).find(
62-
([_, column]) => column.primaryKey,
62+
([_, column]) => column.isPrimaryKey,
6363
)?.[0];
6464

6565
await this.client.query({

src/classes/query.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { QueryParams } from "@clickhouse/client";
22
import type { Client } from ".";
3-
import { AllExpressions } from "../expressions";
3+
import { AllExpressions, OrderByExpression } from "../expressions";
44
import * as conditions from "../expressions/conditions";
55
import { ColumnBuilder } from "../schema/builder";
66
import { ClickhouseJSONResponse, ExtractPropsFromTable } from "../types/clickhouse";
@@ -11,7 +11,9 @@ import { SQLParser, sql } from "../utils/sql";
1111

1212
type GenericParams<T extends Table> = {
1313
where: (columns: T["columns"], conditions: AllExpressions) => SQLParser;
14-
orderBy?: (columns: T["columns"], conditions: AllExpressions) => string;
14+
orderBy?: (columns: T["columns"], conditions: OrderByExpression) => string;
15+
limit?: number;
16+
offset?: number;
1517
};
1618

1719
export class Query<T extends Table> {
@@ -25,13 +27,20 @@ export class Query<T extends Table> {
2527
this.table = table;
2628
}
2729

28-
public async findFirst(params: GenericParams<T>) {
30+
public async findFirst(params: Omit<GenericParams<T>, "limit">) {
2931
const { template, queryParams: query_params } = parseQuery(
3032
params.where(this.table.columns, conditions),
3133
);
3234

3335
const queriedData = await this.client.query({
34-
query: `SELECT * FROM ${this.database}.${this.table.name} WHERE ${template} LIMIT 1`,
36+
query: `SELECT * FROM ${this.database}.${this.table.name} WHERE ${template} ${
37+
params.orderBy
38+
? `ORDER BY ${params.orderBy(this.table.columns, {
39+
asc: conditions.asc,
40+
desc: conditions.desc,
41+
})}`
42+
: ""
43+
} LIMIT 1 OFFSET ${params.offset ?? 0}`,
3544
query_params,
3645
});
3746

@@ -46,7 +55,14 @@ export class Query<T extends Table> {
4655
);
4756

4857
const queriedData = await this.client.query({
49-
query: `SELECT * FROM ${this.database}.${this.table.name} WHERE ${template}`,
58+
query: `SELECT * FROM ${this.database}.${this.table.name} WHERE ${template} ${
59+
params.orderBy
60+
? `ORDER BY ${params.orderBy(this.table.columns, {
61+
asc: conditions.asc,
62+
desc: conditions.desc,
63+
})}`
64+
: ""
65+
} LIMIT ${params.limit ?? 100} OFFSET ${params.offset ?? 0}`,
5066
query_params,
5167
});
5268

src/expressions/conditions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,9 @@ export function arrayOverlaps<T extends ColumnBuilder = ColumnBuilder>(column1:
117117

118118
// Order by
119119
export function asc<T extends ColumnBuilder = ColumnBuilder>(column: T) {
120-
return sql`${column} ASC`;
120+
return `${column.name} ASC`;
121121
}
122122

123123
export function desc<T extends ColumnBuilder = ColumnBuilder>(column: T) {
124-
return sql`${column} DESC`;
124+
return `${column.name} DESC`;
125125
}

src/expressions/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as conditions from "./conditions";
22

3-
export type AllExpressions = typeof conditions;
3+
export type AllExpressions = Omit<typeof conditions, "asc" | "desc">;
4+
export type OrderByExpression = { asc: typeof conditions.asc; desc: typeof conditions.desc };
45

56
export function combineExpression(...conditions: string[]) {
67
return conditions.join(" AND ");

tests/index.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import getCustomer from "./query/complex/getCustomer";
66
import createUser from "./query/createUser";
77
import deleteUser from "./query/deleteUser";
88
import getUser from "./query/getUser";
9+
import limit from "./query/limit";
10+
import offset from "./query/offset";
11+
import ordering from "./query/ordering";
912
import updateUser from "./query/updateUser";
1013
import { testSchemas } from "./testSchemas";
1114
import parseQuery from "./utils/parseQuery";
@@ -51,6 +54,11 @@ describe("Test chorm", () => {
5154
it("should be defined", defined);
5255
it("should have all schemas in .query", checkProperties);
5356

57+
/* Check Ordering */
58+
it("should check ordering", ordering);
59+
it("should check limit", limit);
60+
it("should check offset", offset);
61+
5462
/* Query */
5563
it("should create a user", createUser);
5664
it("should fetch a user", getUser);

tests/query/limit.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { chorm } from "../index.test";
2+
3+
export default async function () {
4+
const testDesc = await chorm.query.ordering.findMany({
5+
where: (columns, { isNotNull }) => isNotNull(columns.num),
6+
limit: 5,
7+
});
8+
9+
expect(testDesc).toBeDefined();
10+
expect(testDesc).toHaveLength(5);
11+
}

tests/query/offset.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { chorm } from "../index.test";
2+
3+
export default async function () {
4+
const testDesc = await chorm.query.ordering.findMany({
5+
where: (columns, { isNotNull }) => isNotNull(columns.num),
6+
offset: 2,
7+
orderBy(columns, conditions) {
8+
return conditions.asc(columns.num);
9+
},
10+
});
11+
12+
expect(testDesc).toBeDefined();
13+
expect(testDesc?.[0]?.num).toBe(2);
14+
}

tests/query/ordering.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { chorm } from "../index.test";
2+
3+
export default async function () {
4+
const userIds = new Array(100).fill(0).map((_, i) => i);
5+
6+
for await (const id of userIds) {
7+
const query = await chorm.query.ordering.insert({
8+
id: id.toString(),
9+
num: id,
10+
});
11+
12+
expect(query).toBeDefined();
13+
}
14+
15+
const testDesc = await chorm.query.ordering.findMany({
16+
where: (columns, { isNotNull }) => isNotNull(columns.num),
17+
orderBy: (columns, { desc }) => desc(columns.num),
18+
});
19+
20+
expect(testDesc).toBeDefined();
21+
expect(testDesc).toHaveLength(100);
22+
expect(testDesc?.[0]?.num).toBe(99);
23+
24+
const testAsc = await chorm.query.ordering.findMany({
25+
where: (columns, { isNotNull }) => isNotNull(columns.num),
26+
orderBy: (columns, { asc }) => asc(columns.num),
27+
});
28+
29+
expect(testAsc).toBeDefined();
30+
expect(testAsc).toHaveLength(100);
31+
expect(testAsc?.[0]?.num).toBe(0);
32+
}

tests/testSchemas.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ export const testSchemas = {
2727
null_column: text("null_column"),
2828
array: array("array", { type: DATA_TYPE.String }).$type<string[]>().notNull(),
2929
}),
30+
ordering: table("ordering", {
31+
id: text("id").primaryKey().notNull(),
32+
num: integer("num").notNull(),
33+
}),
3034
};

0 commit comments

Comments
 (0)