Skip to content

Commit

Permalink
Merge pull request #387 from dojoengine/feat/experimental-update
Browse files Browse the repository at this point in the history
feat: add nested queries to match book + syntactic sugar
  • Loading branch information
MartianGreed authored Jan 31, 2025
2 parents 4ebdb4c + d030ace commit 750229a
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 3 deletions.
15 changes: 15 additions & 0 deletions .changeset/selfish-poets-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@dojoengine/sdk": patch
"@dojoengine/core": patch
"@dojoengine/create-burner": patch
"@dojoengine/create-dojo": patch
"@dojoengine/predeployed-connector": patch
"@dojoengine/react": patch
"@dojoengine/state": patch
"@dojoengine/torii-client": patch
"@dojoengine/torii-wasm": patch
"@dojoengine/utils": patch
"@dojoengine/utils-wasm": patch
---

fix: Add nested query test to match book + syntactic sugar
151 changes: 150 additions & 1 deletion packages/sdk/src/__tests__/clauseBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { describe, expect, it } from "vitest";
import { ClauseBuilder } from "../clauseBuilder";
import {
AndComposeClause,
ClauseBuilder,
MemberClause,
OrComposeClause,
} from "../clauseBuilder";
import {
ComparisonOperator,
LogicalOperator,
Expand Down Expand Up @@ -208,4 +213,148 @@ describe("ClauseBuilder", () => {
});
});
});

it("should handle complex composition", () => {
const clause = new ClauseBuilder()
.compose()
.and([
new ClauseBuilder()
.compose()
.and([
new ClauseBuilder().where(
"world-player",
"score",
"Gt",
100
),
new ClauseBuilder()
.compose()
.or([
new ClauseBuilder().where(
"world-player",
"name",
"Eq",
"Bob"
),
new ClauseBuilder().where(
"world-player",
"name",
"Eq",
"Alice"
),
]),
]),
new ClauseBuilder().where("world-item", "durability", "Lt", 50),
])
.build();

expect(clause).toEqual({
Composite: {
operator: "And",
clauses: [
{
Composite: {
operator: "And",
clauses: [
{
Member: {
model: "world-player",
member: "score",
operator: "Gt" as ComparisonOperator,
value: { Primitive: { U32: 100 } },
},
},

{
Composite: {
operator: "Or",
clauses: [
{
Member: {
model: "world-player",
member: "name",
operator:
"Eq" as ComparisonOperator,
value: {
String: "Bob",
},
},
},

{
Member: {
model: "world-player",
member: "name",
operator:
"Eq" as ComparisonOperator,
value: {
String: "Alice",
},
},
},
],
},
},
],
},
},
{
Member: {
model: "world-item",
member: "durability",
operator: "Lt" as ComparisonOperator,
value: { Primitive: { U32: 50 } },
},
},
],
},
});
});
it("should be nice to use", () => {
const clause = new ClauseBuilder()
.compose()
.and([
new ClauseBuilder()
.compose()
.and([
new ClauseBuilder().where(
"world-player",
"score",
"Gt",
100
),
new ClauseBuilder()
.compose()
.or([
new ClauseBuilder().where(
"world-player",
"name",
"Eq",
"Bob"
),
new ClauseBuilder().where(
"world-player",
"name",
"Eq",
"Alice"
),
]),
]),
new ClauseBuilder().where("world-item", "durability", "Lt", 50),
])
.build();

const nicerClause = AndComposeClause([
AndComposeClause([
MemberClause("world-player", "score", "Gt", 100),
OrComposeClause([
MemberClause("world-player", "name", "Eq", "Bob"),
MemberClause("world-player", "name", "Eq", "Alice"),
]),
]),
MemberClause("world-item", "durability", "Lt", 50),
]).build();

expect(clause).toEqual(nicerClause);
});
});
72 changes: 70 additions & 2 deletions packages/sdk/src/clauseBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import {
import { convertToPrimitive } from "./convertToMemberValue";
import { SchemaType } from "./types";

type ClauseBuilderInterface = {
build(): Clause;
};

// Helper types for nested model structure
type ModelPath<T, K extends keyof T> = K extends string
? T[K] extends Record<string, any>
Expand All @@ -28,6 +32,70 @@ type GetModelType<
: never
: never;

/**
* Saves some keyboard strokes to get a KeysClause.
*
* @param models - the models you want to query, has to be in form of ns-Model
* @param keys - the keys that has the model. You can use `undefined` as a wildcard to match any key
* @param pattern - either VariableLen or FixedLen - to check exact match of key number
* @return ClauseBuilder<T>
*/
export function KeysClause<T extends SchemaType>(
models: ModelPath<T, keyof T>[],
keys: (string | undefined)[],
pattern: PatternMatching = "VariableLen"
): ClauseBuilder<T> {
return new ClauseBuilder<T>().keys(models, keys, pattern);
}

/**
* Saves some keyboard strokes to get a MemberClause.
*
* @template T - the schema type
* @param model - the model you want to query, has to be in form of ns-Model
* @param member - the member of the model on which you want to apply operator
* @param operator - the operator to apply
* @param value - the value to operate on.
* @return ClauseBuilder<T>
*/
export function MemberClause<
T extends SchemaType,
Path extends ModelPath<T, keyof T>,
M extends keyof GetModelType<T, ModelPath<T, keyof T>>,
>(
model: Path,
member: M & string,
operator: ComparisonOperator,
value: GetModelType<T, Path>[M] | GetModelType<T, Path>[M][]
): ClauseBuilder<T> {
return new ClauseBuilder<T>().where(model, member, operator, value);
}

/**
* Saves some keyboard strokes to get a Composite "Or" Clause
*
* @template T - the schema type
* @param clauses - the inner clauses that you want to compose
* @return CompositeBuilder<T>
*/
export function AndComposeClause<T extends SchemaType>(
clauses: ClauseBuilderInterface[]
): CompositeBuilder<T> {
return new ClauseBuilder<T>().compose().and(clauses);
}

/**
* Saves some keyboard strokes to get a Composite "And" Clause
* @template T - the schema type
* @param clauses - the inner clauses that you want to compose
* @return CompositeBuilder<T>
*/
export function OrComposeClause<T extends SchemaType>(
clauses: ClauseBuilderInterface[]
): CompositeBuilder<T> {
return new ClauseBuilder<T>().compose().or(clauses);
}

export class ClauseBuilder<T extends SchemaType> {
private clause: Clause;

Expand Down Expand Up @@ -103,12 +171,12 @@ class CompositeBuilder<T extends Record<string, Record<string, any>>> {
private orClauses: Clause[] = [];
private andClauses: Clause[] = [];

or(clauses: ClauseBuilder<T>[]): CompositeBuilder<T> {
or(clauses: ClauseBuilderInterface[]): CompositeBuilder<T> {
this.orClauses = clauses.map((c) => c.build());
return this;
}

and(clauses: ClauseBuilder<T>[]): CompositeBuilder<T> {
and(clauses: ClauseBuilderInterface[]): CompositeBuilder<T> {
this.andClauses = clauses.map((c) => c.build());
return this;
}
Expand Down

0 comments on commit 750229a

Please sign in to comment.