Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/query/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export abstract class Query<
abstract [__type]: T;

type = undefined as unknown as T["infer"];
/** When true, execute() skips schema parsing (used by RETURN DIFF and FETCH). */
/** When true, result parsing is skipped (used by RETURN DIFF). */
protected _skipParse = false;
/** Type-guard that checks whether a value matches this query's result type. */
validate(value: unknown): value is T["infer"] {
Expand All @@ -41,6 +41,16 @@ export abstract class Query<
return this[__type].parse(value);
}

/**
* Normalize a raw query result into the public return value for this query.
* Most queries parse through the runtime schema, while a few modes such as
* `RETURN DIFF` intentionally bypass parse.
*/
parseResult(value: unknown): T["infer"] {
if (this._skipParse) return value as T["infer"];
return this.parse(value);
}

/** Create a shallow clone of this query. */
clone(): this {
return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
Expand Down Expand Up @@ -107,9 +117,7 @@ export abstract class Query<
query,
ctx.variables,
);
// Skip parse for RETURN DIFF (JsonPatchOp[]) and FETCH (resolved records)
if (this._skipParse) return result as this["type"];
return this.parse(result);
return this.parseResult(result);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/query/batch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class BatchQuery<Q extends Query<any, any>[]> {
// Skip BEGIN (first) and COMMIT (last) results
const queryResults = results.slice(1, results.length - 1);
return queryResults.map((result, i) => {
return this.queries[i]!.parse(result);
return this.queries[i]!.parseResult(result);
}) as BatchResult<Q>;
}

Expand Down
6 changes: 3 additions & 3 deletions src/query/create.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RecordId, Table } from "surrealdb";
import { RecordId, Table, type RecordIdValue } from "surrealdb";
import type { Orm } from "../schema/orm.ts";
import {
type AbstractType,
Expand Down Expand Up @@ -57,12 +57,12 @@ export class CreateQuery<
_modificationMode?: ModificationMode;
private _return?: "none" | "before" | "after" | "diff" | Workable<C>;
private _timeout?: string;
private _id?: string;
private _id?: RecordIdValue;

constructor(
orm: O,
readonly tb: T,
id?: string,
id?: RecordIdValue,
) {
super();
this[__ctx] = {
Expand Down
4 changes: 2 additions & 2 deletions src/query/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ export class SelectQuery<
});

const thing = this.displaySubject(ctx);
const start = this._start && ctx.var(this._start);
const limit = this._limit && ctx.var(this._limit);
const start = this._start !== undefined ? ctx.var(this._start) : undefined;
const limit = this._limit !== undefined ? ctx.var(this._limit) : undefined;

const predicates = this._entry
? /* surql */ `VALUE ${this._entry[__display](ctx)}`
Expand Down
2 changes: 1 addition & 1 deletion src/schema/orm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export class Orm<T extends AnyTable[] = AnyTable[]> {
_C extends WorkableContext<this>,
Tb extends keyof this["tables"] & string,
>(tb: Tb, id?: RecordIdValue) {
return new CreateQuery(this, tb, id?.toString());
return new CreateQuery(this, tb, id);
}

/**
Expand Down
29 changes: 28 additions & 1 deletion tests/unit/query/batch.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { describe, expect, test } from "bun:test";
import { Surreal } from "surrealdb";
import { Surreal, type SurrealSession } from "surrealdb";
import { __display, displayContext, orm, t, table } from "../../../src";

function mockSurrealQuery(results: unknown[]): SurrealSession {
return {
query: () => Promise.resolve(results),
} as unknown as SurrealSession;
}

describe("BATCH queries", () => {
const user = table("user", {
name: t.string(),
Expand Down Expand Up @@ -113,6 +119,27 @@ describe("BATCH queries", () => {
expect(display).toContain("WHERE");
});

test("reuses single-query parse behavior for RETURN DIFF", async () => {
const surreal = mockSurrealQuery([
{ status: "OK" },
[{ op: "replace", path: "/age", value: 31 }],
{ status: "OK" },
]);
const batchDb = orm(surreal, user, post);

const result = (await batchDb
.batch(batchDb.update("user", "alice").set({ age: 31 }).return("diff"))
.execute()) as unknown as [
Array<{
op: string;
path: string;
value: number;
}>,
];

expect(result[0]).toEqual([{ op: "replace", path: "/age", value: 31 }]);
});

test("Orm has batch method", () => {
expect(db).toHaveProperty("batch");
expect(typeof db.batch).toBe("function");
Expand Down
23 changes: 22 additions & 1 deletion tests/unit/query/create.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import { Surreal } from "surrealdb";
import { RecordId, Surreal } from "surrealdb";
import { __display, displayContext, orm, t, table } from "../../../src";

describe("CREATE queries", () => {
Expand Down Expand Up @@ -40,6 +40,27 @@ describe("CREATE queries", () => {
// ID is in variables, not in the query string directly
});

test("preserves composite CREATE ids without stringifying them", () => {
const compositeId = {
tenant: "acme",
slug: "alice",
};
const query = db.create("user", compositeId).set({
name: "Alice",
age: 25,
});
const ctx = displayContext();

query[__display](ctx);

const recordId = Object.values(ctx.variables).find(
(value) => value instanceof RecordId,
);

expect(recordId).toBeInstanceOf(RecordId);
expect((recordId as RecordId).id).toEqual(compositeId);
});

test("generates CREATE with CONTENT", () => {
const query = db.create("user").content({
name: "Bob",
Expand Down
20 changes: 20 additions & 0 deletions tests/unit/query/select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ describe("SELECT queries", () => {
expect(result).toContain("LIMIT");
});

test("generates SELECT with LIMIT 0", () => {
const query = db.select("user").limit(0);
const ctx = displayContext();
const result = query[__display](ctx);

expect(result).toContain("SELECT * FROM");
expect(result).toContain("LIMIT");
expect(Object.values(ctx.variables)).toContainEqual(0);
});

test("generates SELECT with START", () => {
const query = db.select("user").start(5);
const ctx = displayContext();
Expand All @@ -66,6 +76,16 @@ describe("SELECT queries", () => {
expect(result).toContain("START");
});

test("generates SELECT with START 0", () => {
const query = db.select("user").start(0);
const ctx = displayContext();
const result = query[__display](ctx);

expect(result).toContain("SELECT * FROM");
expect(result).toContain("START");
expect(Object.values(ctx.variables)).toContainEqual(0);
});

test("generates SELECT with START and LIMIT", () => {
const query = db.select("user").start(10).limit(20);
const ctx = displayContext();
Expand Down