Skip to content

Commit

Permalink
feat: add querybuilder
Browse files Browse the repository at this point in the history
feat: add basic querybuilder
  • Loading branch information
MartianGreed committed Nov 26, 2024
1 parent cd9db9d commit 7a34902
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
36 changes: 36 additions & 0 deletions packages/sdk/src/__tests__/queryBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from "vitest";
import { QueryBuilder } from "../queryBuilder";

describe("QueryBuilder", () => {
it("should be implemented", () => {
const query = new QueryBuilder().build();
expect(query).toStrictEqual({});
});

it("should work with example", () => {
const query = new QueryBuilder()
.namespace("world", (n) =>
n.entity("player", (e) => e.eq("id", "1").eq("name", "Alice"))
)
.namespace("universe", (n) =>
n.entity("galaxy", (e) => e.is("name", "Milky Way"))
)
.build();
const expected = {
world: {
player: {
$: { where: { id: { $eq: "1" }, name: { $eq: "Alice" } } },
},
},
universe: {
galaxy: {
$: {
where: { name: { $is: "Milky Way" } },
},
},
},
};

expect(query).toStrictEqual(expected);
});
});
1 change: 1 addition & 0 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SchemaType, SDK, SDKConfig, UnionOfModelData } from "./types";

export * from "./types";
export * from "./state";
export * from "./queryBuilder";

/**
* Creates a new Torii client instance.
Expand Down
153 changes: 153 additions & 0 deletions packages/sdk/src/queryBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { QueryType, SchemaType } from "./types";
export class QueryBuilder<T extends SchemaType> {
namespaces: Map<string, Namespace<T>>;

constructor() {
this.namespaces = new Map<string, Namespace<T>>();
}

public namespace(
name: string,
cb: (ns: Namespace<T>) => void
): Namespace<T> {
const ns = new Namespace(this, name);
this.namespaces.set(name, ns);
cb(ns);
return ns;
}

public build(): QueryType<T> {
const qt: Record<
string,
Record<
string,
{ $: { where: Record<string, Record<string, any>> } }
>
> = {};
for (const [ns, namespace] of this.namespaces) {
qt[ns] = {};
for (const [entity, entityObj] of namespace.entities) {
const constraints: Record<string, Record<string, any>> = {};
for (const [field, constraint] of entityObj.constraints) {
constraints[field] = {
[`${constraint.operator}`]: constraint.value,
};
}
qt[ns][entity] = {
$: {
where: {
...(qt[ns]?.[entity]?.$?.where ?? {}),
...constraints,
},
},
};
}
}
return qt as QueryType<T>;
}
}

class Namespace<T extends SchemaType> {
entities: Map<string, QueryEntity<T>>;

constructor(
private parent: QueryBuilder<T>,
private name: string
) {
this.entities = new Map<string, QueryEntity<T>>();
}

public entity(
name: string,
cb: (entity: QueryEntity<T>) => void
): QueryEntity<T> {
const entity = new QueryEntity(this, name);
this.entities.set(name, entity);
cb(entity);
return entity;
}

public namespace(ns: string, cb: (ns: Namespace<T>) => void): Namespace<T> {
return this.parent.namespace(ns, cb);
}

public build(): QueryType<T> {
return this.parent.build();
}
}

class QueryEntity<T extends SchemaType> {
constraints: Map<string, Constraint<T>>;

constructor(
private parent: Namespace<T>,
private name: string
) {
this.constraints = new Map<string, Constraint<T>>();
}

public is(field: string, value: any): QueryEntity<T> {
this.constraints.set(field, new Constraint(this, Operator.is, value));
return this;
}

public eq(field: string, value: any): QueryEntity<T> {
this.constraints.set(field, new Constraint(this, Operator.eq, value));
return this;
}

public neq(field: string, value: any): QueryEntity<T> {
this.constraints.set(field, new Constraint(this, Operator.neq, value));
return this;
}

public gt(field: string, value: any): QueryEntity<T> {
this.constraints.set(field, new Constraint(this, Operator.gt, value));
return this;
}

public gte(field: string, value: any): QueryEntity<T> {
this.constraints.set(field, new Constraint(this, Operator.gte, value));
return this;
}

public lt(field: string, value: any): QueryEntity<T> {
this.constraints.set(field, new Constraint(this, Operator.lt, value));
return this;
}

public lte(field: string, value: any): QueryEntity<T> {
this.constraints.set(field, new Constraint(this, Operator.lte, value));
return this;
}

public build(): QueryType<T> {
return this.parent.build();
}
}

class Constraint<T extends SchemaType> {
constructor(
private parent: QueryEntity<T>,
private _operator: Operator,
private _value: any
) {}

get operator(): string {
return this._operator.toString();
}

get value(): any {
return this._value;
}
}

enum Operator {
is = "$is",
eq = "$eq",
neq = "$neq",
gt = "$gt",
gte = "$gte",
lt = "$lt",
lte = "$lte",
}

0 comments on commit 7a34902

Please sign in to comment.