Skip to content

Commit

Permalink
rename query fragments
Browse files Browse the repository at this point in the history
  • Loading branch information
alvrs committed Aug 30, 2024
1 parent b5d2ebf commit b4afbd5
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 26 deletions.
12 changes: 6 additions & 6 deletions packages/stash/src/actions/runQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { runQuery } from "./runQuery";
import { defineStore } from "@latticexyz/store";
import { Stash, StoreRecords, getQueryConfig } from "../common";
import { setRecord } from "./setRecord";
import { In, MatchRecord, NotIn, NotMatchRecord } from "../queryFragments";
import { In, Matches, NotIn, NotMatches } from "../queryFragments";
import { Hex } from "viem";

describe("runQuery", () => {
Expand Down Expand Up @@ -79,7 +79,7 @@ describe("runQuery", () => {
});

it("should return all keys that have Position.x = 4 and are included in Health", () => {
const result = runQuery({ stash, query: [MatchRecord(Position, { x: 4 }), In(Health)] });
const result = runQuery({ stash, query: [Matches(Position, { x: 4 }), In(Health)] });
attest(result).snap({ keys: { "0x4": { player: "0x4" } } });
});

Expand All @@ -95,7 +95,7 @@ describe("runQuery", () => {
});

it("should return all keys that don't include a gold item in the Inventory table", () => {
const result = runQuery({ stash, query: [NotMatchRecord(Inventory, { item: "0xgold" })] });
const result = runQuery({ stash, query: [NotMatches(Inventory, { item: "0xgold" })] });
attest(result).snap({
keys: {
"0x0|0xsilver": { player: "0x0", item: "0xsilver" },
Expand All @@ -108,9 +108,9 @@ describe("runQuery", () => {
});

it("should throw an error when tables with different key schemas are mixed", () => {
attest(() =>
runQuery({ stash, query: [In(Position), MatchRecord(Inventory, { item: "0xgold", amount: 2 })] }),
).throws("All tables in a query must share the same key schema");
attest(() => runQuery({ stash, query: [In(Position), Matches(Inventory, { item: "0xgold", amount: 2 })] })).throws(
"All tables in a query must share the same key schema",
);
});

it("should include all matching records from the tables if includeRecords is set", () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/stash/src/actions/runQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function runQuery<query extends Query, options extends RunQueryOptions>({
for (const fragment of query) {
// TODO: this might be more efficient if we would use a Map() instead of an object
for (const encodedKey of Object.keys(keys)) {
if (!fragment.match(stash, encodedKey)) {
if (!fragment.pass(stash, encodedKey)) {
delete keys[encodedKey];
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/stash/src/actions/subscribeQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { beforeEach, describe, it, vi, expect } from "vitest";
import { QueryUpdate, subscribeQuery } from "./subscribeQuery";
import { attest } from "@arktype/attest";
import { defineStore } from "@latticexyz/store";
import { In, MatchRecord } from "../queryFragments";
import { In, Matches } from "../queryFragments";
import { deleteRecord } from "./deleteRecord";
import { setRecord } from "./setRecord";
import { Stash } from "../common";
Expand Down Expand Up @@ -66,7 +66,7 @@ describe("defineQuery", () => {
it("should notify subscribers when a matching key is updated", () => {
let lastUpdate: unknown;
const subscriber = vi.fn((update: QueryUpdate) => (lastUpdate = update));
const result = subscribeQuery({ stash, query: [MatchRecord(Position, { x: 4 }), In(Health)] });
const result = subscribeQuery({ stash, query: [Matches(Position, { x: 4 }), In(Health)] });
result.subscribe(subscriber);

setRecord({ stash, table: Position, key: { player: "0x4" }, record: { y: 2 } });
Expand Down
4 changes: 2 additions & 2 deletions packages/stash/src/actions/subscribeQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function subscribeQuery<query extends Query>({
update.keys[key] = matching[key];
// If the key matched before, check if the relevant fragments (accessing this table) still match
const relevantFragments = query.filter((f) => f.table.namespace === namespaceLabel && f.table.label === label);
const match = relevantFragments.every((f) => f.match(stash, key));
const match = relevantFragments.every((f) => f.pass(stash, key));
if (match) {
// If all relevant fragments still match, the key still matches the query.
update.types[key] = "update";
Expand All @@ -105,7 +105,7 @@ export function subscribeQuery<query extends Query>({
}
} else {
// If this key didn't match the query before, check all fragments
const match = query.every((f) => f.match(stash, key));
const match = query.every((f) => f.pass(stash, key));
if (match) {
// Since the key schema of query fragments has to match, we can pick any fragment to decode they key
const decodedKey = decodeKey({ stash, table: query[0].table, encodedKey: key });
Expand Down
28 changes: 13 additions & 15 deletions packages/stash/src/queryFragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { TableRecord } from "./common";
import { getRecords } from "./actions/getRecords";
import { getKeys } from "./actions/getKeys";

// TODO: add more query fragments - ie GreaterThan, LessThan, Range, etc

/**
* Compare two {@link TableRecord}s.
* `a` can be a partial record, in which case only the keys present in `a` are compared to the corresponding keys in `b`.
Expand Down Expand Up @@ -36,7 +34,7 @@ export type QueryFragment<table extends Table = Table> = {
/**
* Checking an individual table row for whether it matches the query fragment
*/
match: (stash: Stash, encodedKey: string) => boolean;
pass: (stash: Stash, encodedKey: string) => boolean;
/**
* The keys that should be included in the query result if this is the first fragment in the query.
* This is to avoid having to iterate over each key in the first table if there is a more efficient
Expand All @@ -50,38 +48,38 @@ export type QueryFragment<table extends Table = Table> = {
* RECS equivalent: Has(Component)
*/
export function In<table extends Table>(table: table): QueryFragment<table> {
const match = (stash: Stash, encodedKey: string) => encodedKey in getRecords({ stash, table });
const pass = (stash: Stash, encodedKey: string) => encodedKey in getRecords({ stash, table });
const getInitialKeys = (stash: Stash) => getKeys({ stash, table });
return { table, match, getInitialKeys };
return { table, pass, getInitialKeys };
}

/**
* Matches all records that don't exist in the table.
* RECS equivalent: Not(Component)
*/
export function NotIn<table extends Table>(table: table): QueryFragment<table> {
const match = (stash: Stash, encodedKey: string) => !(encodedKey in getRecords({ stash, table }));
const pass = (stash: Stash, encodedKey: string) => !(encodedKey in getRecords({ stash, table }));
const getInitialKeys = () => ({});
return { table, match, getInitialKeys };
return { table, pass, getInitialKeys };
}

/**
* Matches all records that match the provided partial record.
* This works for both value and key, since both are part of the record.
* RECS equivalent (only for value match): HasValue(Component, value)
*/
export function MatchRecord<table extends Table>(
export function Matches<table extends Table>(
table: table,
partialRecord: Partial<TableRecord<table>>,
): QueryFragment<table> {
const match = (stash: Stash, encodedKey: string) => {
const pass = (stash: Stash, encodedKey: string) => {
const record = getRecords({ stash, table })[encodedKey];
return recordMatches(partialRecord, record);
};
// TODO: this is a very naive and inefficient implementation for large tables, can be optimized via indexer tables
const getInitialKeys = (stash: Stash) =>
Object.fromEntries(Object.entries(getKeys({ stash, table })).filter(([key]) => match(stash, key)));
return { table, match, getInitialKeys };
Object.fromEntries(Object.entries(getKeys({ stash, table })).filter(([key]) => pass(stash, key)));
return { table, pass, getInitialKeys };
}

/**
Expand All @@ -91,16 +89,16 @@ export function MatchRecord<table extends Table>(
* @param table
* @param partialRecord
*/
export function NotMatchRecord<table extends Table>(
export function NotMatches<table extends Table>(
table: table,
partialRecord: Partial<TableRecord<table>>,
): QueryFragment<table> {
const match = (stash: Stash, encodedKey: string) => {
const pass = (stash: Stash, encodedKey: string) => {
const record = getRecords({ stash, table })[encodedKey];
return !recordMatches(partialRecord, record);
};
// TODO: this is a very naive and inefficient implementation for large tables, can be optimized via indexer tables
const getInitialKeys = (stash: Stash) =>
Object.fromEntries(Object.entries(getKeys({ stash, table })).filter(([key]) => match(stash, key)));
return { table, match, getInitialKeys };
Object.fromEntries(Object.entries(getKeys({ stash, table })).filter(([key]) => pass(stash, key)));
return { table, pass, getInitialKeys };
}

0 comments on commit b4afbd5

Please sign in to comment.