diff --git a/docs/pages/indexer/sql.mdx b/docs/pages/indexer/sql.mdx index 345ca9491f..7f096d7667 100644 --- a/docs/pages/indexer/sql.mdx +++ b/docs/pages/indexer/sql.mdx @@ -1,6 +1,6 @@ import { CollapseCode } from "../../components/CollapseCode"; import FilterTypes from "../../components/common-text/FilterTypes.mdx"; -import { Callout } from "nextra/components"; +import { Callout, Tabs, Tab } from "nextra/components"; # SQL API @@ -24,293 +24,853 @@ The query language is a subset of [the SQL `SELECT` command]( + + You can run SQL queries by communicating directly with the indexer's API, for example using [curl](https://curl.se/). + To do so: + + 1. Create a file, `query.json`, which includes the `World` address and query. + For example, you can use this file: + + ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT id, description FROM app__Tasks WHERE completedAt = 0 limit 2" + } + ] + ``` + + 1. Run this command (assuming you query from the Garnet chain). Install `curl` and `jq` first if necessary. + + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` + + The output is a mapping with two fields, the block height for which the result is valid, and the result itself. + The result is a list of query responses, here it contains just one item because we only submitted a single query. + The query response is also a list. + The first entry is the field names, and all the other entries are rows returned by `SELECT`. + + ``` + { + "block_height": 5699682, + "result": [ + [ + [ + "id", + "description" + ], + [ + "0x3100000000000000000000000000000000000000000000000000000000000000", + "Walk the dog" + ], + [ + "0x3e0a112aadc5e02927fb4a91649bea565fd1baa1175aae4cb4957d6348f165cf", + "Test" + ], + ] + ] + } + ``` + + Here we only care about the first result, so from now on we use this command line to tell `jq` to only show us that information. + + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq '.result[0]' + ``` + + + + Even if you choose not to use MUD client synchronization, you can query the indexer from [Typescript](https://www.typescriptlang.org/), using the same `selectFrom` you can also use to create [initial hydration filters](#mud-state-hydration-via-sql-api). + + + + This API is still experimental and may change. -You can run SQL queries by communicating directly with the server's API, for example using [curl](https://curl.se/). + + + 1. Create the project (in an empty directory) and install the software. + + ```sh copy + pnpm create ts-node + pnpm install + ``` + + 1. Add the package that includes the SQL interface library. + + ```sh copy + pnpm install @latticexyz/store-sync @latticexyz/store + ``` + + 1. Replace `src/main.ts` with this file. + + ```ts filename="main.ts" copy + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; + import { defineStore } from "@latticexyz/store"; + + const config = defineStore({ + namespace: "app", + tables: { + Tasks: { + schema: { + id: "bytes32", + createdAt: "uint256", + completedAt: "uint256", + description: "string", + }, + key: ["id"], + }, + Creator: { + schema: { + id: "bytes32", + taskCreator: "address", + }, + key: ["id"], + }, + }, + }); + + const queryUncompleted = selectFrom({ + table: config.tables.app__Tasks, + where: "completedAt = 0", + limit: 2, + }); + + const queryResult = await fetchRecords({ + indexerUrl: "https://indexer.mud.garnetchain.com/q", + storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + queries: [queryUncompleted], + }); + + console.log(`SQL: ${queryUncompleted.sql}\nResult:`); + console.log(JSON.stringify( + queryResult, + (_, v) => typeof v === 'bigint' ? v.toString() : v, + 2 + )) + ``` + +
+ + Explanation + + ```ts + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; + import { defineStore } from "@latticexyz/store"; + ``` + + Import the necessary definitions. + + ```typescript + const config = defineStore({ + namespace: "app", + tables: { + ... + }, + }) + ``` + + Create the table configuration. + The input to `defineStore` is the same as used in the [the `mud.config.ts` file](/config). + + ```typescript + const queryUncompleted = selectFrom({ + table: config.tables.app__Tasks, + where: "completedAt = 0", + limit: 2, + }); + ``` + + Create a query using [`selectFrom`](https://github.com/latticexyz/mud/blob/main/packages/store-sync/src/sql/selectFrom.ts). + The queries supported by `selectFrom` are a subset of those SQL supports. + The results here come from a single table, and only `WHERE` and `LIMIT` clauses are supported "natively" + + ```typescript + const queryResult = await fetchRecords({ + indexerUrl: "https://indexer.mud.garnetchain.com/q", + storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + queries: [queryUncompleted], + }); + ``` + + Run the query. + + ```typescript + console.log("\n\nTwo uncompleted tasks"); + console.log(`SQL: ${queryUncompleted.sql}\nResult:`); + ``` + + The SQL query that generated the resulting records. + + ```typescript + console.log(`SQL: ${queryUncompleted.sql}\nResult:`); + console.log(JSON.stringify( + queryResult, + (_, v) => typeof v === 'bigint' ? v.toString() : v, + 2 + )) + ``` + + Show the SQL query and the actual records. + +
+ + 1. Compile and execute the application. + + ```sh copy + pnpm build && pnpm start + ``` + + The output is a mapping with two fields, the block height for which the result is valid, and the result itself. + The result is a list of query responses, here it contains just one item because we only submitted a single query. + The query result itself is a mapping, with two fields: `table` which is the table information. including the schema, and `records` with the results. + + ```json + SQL: select "id", "createdAt", "completedAt", "description" from app__Tasks where completedAt = 0 limit 2 + Result: + { + "blockHeight": "7256696", + "result": [ + { + "table": { + "label": "Tasks", + "type": "table", + "namespace": "app", + "name": "Tasks", + "tableId": "0x746261707000000000000000000000005461736b730000000000000000000000", + "schema": { + "id": { + "type": "bytes32", + "internalType": "bytes32" + }, + "createdAt": { + "type": "uint256", + "internalType": "uint256" + }, + "completedAt": { + "type": "uint256", + "internalType": "uint256" + }, + "description": { + "type": "string", + "internalType": "string" + } + }, + "key": [ + "id" + ], + "codegen": { + "outputDirectory": "tables", + "tableIdArgument": false, + "storeArgument": false, + "dataStruct": true + }, + "deploy": { + "disabled": false + } + }, + "records": [ + { + "id": "0x3100000000000000000000000000000000000000000000000000000000000000", + "createdAt": "1723495628", + "completedAt": "0", + "description": "Walk the dog" + }, + { + "id": "0x574e6dc7dfad7c446782c889ddc3a0d3d511494de75d0e79bd6f98373bc9e82c", + "createdAt": "1725474162", + "completedAt": "0", + "description": "Create a todo list" + } + ] + } + ] + } + ``` + +
+ ### Simple query This query looks for some fields from a single table. -1. Create a file, `query.json`, with this content. - - ```json filename="query.json" copy - [ - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT id, description FROM app__Tasks" - } - ] - ``` - - The API does not support `SELECT * FROM `, you have to specify column names. +The SQL API does not support `SELECT * FROM
`, you _have_ to specify column names. +In the case of Typescript `selectFrom`, the Typescript code specifies all the columns for you. -1. Run this command. Install `curl` and `jq` first if necessary. + + + + ```sql + SELECT id, description FROM app_Tasks + ``` + + + + + 1. Replace `query.json`. + + ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT id, description FROM app__Tasks" + } + ] + ``` + + 1. Run the query. + + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` + + + + + 1. Create the Typescript application [shown above](#queries). + + 1. Replace `src/main.ts`. + + + + ```ts filename="main.ts" copy showLineNumbers {26-28} + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; + import { defineStore } from "@latticexyz/store"; + + const config = defineStore({ + namespace: "app", + tables: { + Tasks: { + schema: { + id: "bytes32", + createdAt: "uint256", + completedAt: "uint256", + description: "string", + }, + key: ["id"], + }, + Creator: { + schema: { + id: "bytes32", + taskCreator: "address", + }, + key: ["id"], + }, + }, + }); + + const query = selectFrom({ + table: config.tables.app__Tasks, + }); + + const queryResult = await fetchRecords({ + indexerUrl: "https://indexer.mud.garnetchain.com/q", + storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + queries: [query], + }); + + console.log(`SQL: ${query.sql}\nResult:`); + console.log(JSON.stringify(queryResult, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2)); + ``` + + + + 1. Run the application. + + ```sh copy + pnpm build && pnpm start + ``` + + + - ```sh copy - curl https://indexer.mud.garnetchain.com/q --compressed \ - -H 'Accept-Encoding: gzip' \ - -H 'Content-Type: application/json' \ - -d @query.json | jq - ``` +### Conditions -The output is a mapping with two fields, the block height for which the result is valid, and the result itself. -The result is a list of query responses, here it contains just one item because we only submitted a single query. -The query response is also a list. -The first entry is the field names, and all the other entries are rows returned by `SELECT`. +If we want to see only those tasks that haven't been completed we can use a `WHERE` clause. -``` -{ - "block_height": 5699682, - "result": [ - [ - [ - "id", - "description" - ], - [ - "0x3100000000000000000000000000000000000000000000000000000000000000", - "Walk the dog" - ], - [ - "0x3e0a112aadc5e02927fb4a91649bea565fd1baa1175aae4cb4957d6348f165cf", - "Test" - ], - ] - ] -} -``` + + + + ```sql + SELECT id, description FROM app__Tasks WHERE completedAt=0 + ``` + + + + + 1. Replace `query.json`. + + ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT id, description FROM app__Tasks WHERE completedAt=0" + } + ] + ``` + + 1. Run the query. + + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` + + + + + 1. Create the Typescript application [shown above](#queries). + + 1. Replace `src/main.ts`. + + + + ```ts filename="main.ts" copy showLineNumbers {26-29} + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; + import { defineStore } from "@latticexyz/store"; + + const config = defineStore({ + namespace: "app", + tables: { + Tasks: { + schema: { + id: "bytes32", + createdAt: "uint256", + completedAt: "uint256", + description: "string", + }, + key: ["id"], + }, + Creator: { + schema: { + id: "bytes32", + taskCreator: "address", + }, + key: ["id"], + }, + }, + }); + + const query = selectFrom({ + table: config.tables.app__Tasks, + where: "completedAt=0" + }); + + const queryResult = await fetchRecords({ + indexerUrl: "https://indexer.mud.garnetchain.com/q", + storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + queries: [query], + }); + + console.log(`SQL: ${query.sql}\nResult:`); + console.log(JSON.stringify(queryResult, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2)); + ``` + + + + 1. Run the application. + + ```sh copy + pnpm build && pnpm start + ``` + + + -Here we only care about the first result, so from now on we use this command line to tell `jq` to only show us that information. +### Limited results -```sh copy -curl https://indexer.mud.garnetchain.com/q --compressed \ - -H 'Accept-Encoding: gzip' \ - -H 'Content-Type: application/json' \ - -d @query.json | jq '.result[0]' -``` +If you only want to see a few results, you can use a `LIMIT` clause. -### Conditions + + + + ```sql + SELECT id, description FROM app__Tasks LIMIT 3 + ``` + + + + + 1. Replace `query.json`. + + ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT id, description FROM app__Tasks" + } + ] + ``` + + 1. Run the query. + + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` + + + + + 1. Create the Typescript application [shown above](#queries). + + 1. Replace `src/main.ts`. + + + + ```ts filename="main.ts" copy showLineNumbers {26-29} + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; + import { defineStore } from "@latticexyz/store"; + + const config = defineStore({ + namespace: "app", + tables: { + Tasks: { + schema: { + id: "bytes32", + createdAt: "uint256", + completedAt: "uint256", + description: "string", + }, + key: ["id"], + }, + Creator: { + schema: { + id: "bytes32", + taskCreator: "address", + }, + key: ["id"], + }, + }, + }); + + const query = selectFrom({ + table: config.tables.app__Tasks, + limit: 3 + }); + + const queryResult = await fetchRecords({ + indexerUrl: "https://indexer.mud.garnetchain.com/q", + storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + queries: [query], + }); + + console.log(`SQL: ${query.sql}\nResult:`); + console.log(JSON.stringify(queryResult, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2)); + ``` + + + + 1. Run the application. + + ```sh copy + pnpm build && pnpm start + ``` + + + -If we want to see only those tasks that haven't been completed we can use a `WHERE` clause. +You can use `OFFSET` to get a paging effect. +For example, if you use this query you'll get the last result from the previous query and another one. + + + + + ```sql + SELECT id, description FROM app__Tasks LIMIT 2 OFFSET 2 + ``` + + + + + 1. Replace `query.json`. + + ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT id, description FROM app__Tasks LIMIT 2 OFFSET 2" + } + ] + ``` + + 1. Run the query. + + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` + + + + + This feature is not officially supported by `selectFrom`. + However, because `selectFrom` creates an SQL query we can put the clause in the `where` fields as a workaround. + + 1. Create the Typescript application [shown above](#queries). + + 1. Replace `src/main.ts`. + + + + ```ts filename="main.ts" copy showLineNumbers {26-29} + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; + import { defineStore } from "@latticexyz/store"; + + const config = defineStore({ + namespace: "app", + tables: { + Tasks: { + schema: { + id: "bytes32", + createdAt: "uint256", + completedAt: "uint256", + description: "string", + }, + key: ["id"], + }, + Creator: { + schema: { + id: "bytes32", + taskCreator: "address", + }, + key: ["id"], + }, + }, + }); + + const query = selectFrom({ + table: config.tables.app__Tasks, + where: "true limit 2 offset 2" + }); + + const queryResult = await fetchRecords({ + indexerUrl: "https://indexer.mud.garnetchain.com/q", + storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + queries: [query], + }); + + console.log(`SQL: ${query.sql}\nResult:`); + console.log(JSON.stringify(queryResult, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2)); + ``` + + + + 1. Run the application. + + ```sh copy + pnpm build && pnpm start + ``` + + + + -```json filename="query.json" copy -[ - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT id, description FROM app__Tasks WHERE completedAt=0" - } -] -``` +### Sorted results -
+If you want to control the order in which you get results, you can use an `ORDER BY` clause. -Results + + -```json -[ - ["id", "description"], - ["0x3100000000000000000000000000000000000000000000000000000000000000", "Walk the dog"], - ["0x3e0a112aadc5e02927fb4a91649bea565fd1baa1175aae4cb4957d6348f165cf", "Test"] -] -``` + ```sql + SELECT description, createdAt FROM app__Tasks ORDER BY createdAt + ``` -
+ + -### Limited results + 1. Replace `query.json`. -If you only want to see a few results, you can use a `LIMIT` clause. + ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT description, createdAt FROM app__Tasks ORDER BY createdAt" + } + ] + ``` -```json filename="query.json" copy -[ - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT id, description FROM app__Tasks LIMIT 2" - } -] -``` + 1. Run the query. -
+ ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` -Results + -```json -[ - ["id", "description"], - ["0x3100000000000000000000000000000000000000000000000000000000000000", "Walk the dog"], - ["0x3e0a112aadc5e02927fb4a91649bea565fd1baa1175aae4cb4957d6348f165cf", "Test"] -] -``` + -
+ This feature is not officially supported by `selectFrom`. + However, because `selectFrom` creates an SQL query we can put the clause in the `where` fields as a workaround. -You can use `OFFSET` to get a paging effect. -For example, if you use this `query.json` you get two results, and the last row of the first one is repeated as the first row of the second one. + 1. Create the Typescript application [shown above](#queries). -```json filename="query.json" copy -[ - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT id, description FROM app__Tasks LIMIT 3" - }, - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT id, description FROM app__Tasks LIMIT 3 OFFSET 2" - } -] -``` + 1. Replace `src/main.ts`. -
+ -Results + ```ts filename="main.ts" copy showLineNumbers {26-29} + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; + import { defineStore } from "@latticexyz/store"; -Use this command to see the results of both queries. + const config = defineStore({ + namespace: "app", + tables: { + Tasks: { + schema: { + id: "bytes32", + createdAt: "uint256", + completedAt: "uint256", + description: "string", + }, + key: ["id"], + }, + Creator: { + schema: { + id: "bytes32", + taskCreator: "address", + }, + key: ["id"], + }, + }, + }); -```sh copy -curl https://indexer.mud.garnetchain.com/q --compressed \ - -H 'Accept-Encoding: gzip' \ - -H 'Content-Type: application/json' -d @query.json \ - | jq '.result' -``` + const query = selectFrom({ + table: config.tables.app__Tasks, + where: "true ORDER BY \"createdAt\"" + }); -The result is: + const queryResult = await fetchRecords({ + indexerUrl: "https://indexer.mud.garnetchain.com/q", + storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + queries: [query], + }); -```json -[ - [ - ["id", "description"], - ["0x3100000000000000000000000000000000000000000000000000000000000000", "Walk the dog"], - ["0x3e0a112aadc5e02927fb4a91649bea565fd1baa1175aae4cb4957d6348f165cf", "Test"], - ["0xb15fd0e41ab0bb6eb992e0a3d4f30fce6ee24a5fc9c30f725fdfc96d9d16ed95", "Do the dishes"] - ], - [ - ["id", "description"], - ["0xb15fd0e41ab0bb6eb992e0a3d4f30fce6ee24a5fc9c30f725fdfc96d9d16ed95", "Do the dishes"], - ["0xb81d5036d0b62e0f2536635cbd5d7cec1d1f0706c0c6c1a9fa74293d7b0888eb", "Take out the trash"] - ] -] -``` + console.log(`SQL: ${query.sql}\nResult:`); + console.log(JSON.stringify(queryResult, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2)); + ``` -
+ -### Sorted results + 1. Run the application. -If you want to control the order in which you get results, you can use an `ORDER BY` clause. + ```sh copy + pnpm build && pnpm start + ``` -```json filename="query.json" copy -[ - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT description, createdAt FROM app__Tasks ORDER BY createdAt" - } -] -``` +
-Note that the sort field(s) need to be part of the selected columns. + -
+### Multiple tables -Results +You can join multiple tables, using the same syntax SQL uses. -```json -[ - ["description", "createdat"], - ["Walk the dog", "1723495628"], - ["Take out the trash", "1723495640"], - ["Do the dishes", "1723495642"], - ["Test", "1723495964"], - ["Test from a different account", "1723576522"], - ["Another test", "1723576522"], - ["Yet another test", "1723646440"] -] -``` +This feature is not yet supported by `selectFrom`. -
+ + -### Multiple tables + ```sql + SELECT app__Creator.id, description, taskCreator FROM app__Tasks, app__Creator WHERE app__Creator.id=app__Tasks.id + ``` -You can join multiple tables, using the same syntax SQL uses. + + -```json filename="query.json" copy -[ - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT app__Creator.id, description, taskCreator FROM app__Tasks, app__Creator WHERE app__Creator.id=app__Tasks.id" - } -] -``` + 1. Replace `query.json`. -
+ ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT app__Creator.id, description, taskCreator FROM app__Tasks, app__Creator WHERE app__Creator.id=app__Tasks.id" + } + ] + ``` -Results + 1. Run the query. -```json -[ - ["id", "description", "taskcreator"], - [ - "0x3e0a112aadc5e02927fb4a91649bea565fd1baa1175aae4cb4957d6348f165cf", - "Test", - "0x735b2f2c662ebedffa94027a7196f0559f7f18a4" - ], - [ - "0x727d7bfe00b6db638c69595059dc10e21c52a7912d090905a7c7dc8659efd3b8", - "Test from a different account", - "0x428b1853e5ec29d35c84a218ec5170efc7621b58" - ], - [ - "0xb15fd0e41ab0bb6eb992e0a3d4f30fce6ee24a5fc9c30f725fdfc96d9d16ed95", - "Do the dishes", - "0x8225d72f2c39f3729d7f3fc03c6aa8731eaeef48" - ], - [ - "0xb81d5036d0b62e0f2536635cbd5d7cec1d1f0706c0c6c1a9fa74293d7b0888eb", - "Take out the trash", - "0x8225d72f2c39f3729d7f3fc03c6aa8731eaeef48" - ], - [ - "0xd43394ecf79077f65cd83b534dd44d3b4e9e2aa553e95aafecd14b8529543cda", - "Another test", - "0x428b1853e5ec29d35c84a218ec5170efc7621b58" - ] -] -``` + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` -
+
+ +
### Grouping results You can use `GROUP BY` to identify different groups. For example, this query gets you the different task creators. -```json filename="query.json" copy -[ - { - "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", - "query": "SELECT taskCreator FROM app__Creator GROUP BY taskCreator" - } -] -``` +This feature is not yet supported by `selectFrom`. -
+ + -Results + ```sql + SELECT taskCreator FROM app__Creator GROUP BY taskCreator + ``` -```json -[ - ["taskcreator"], - ["0x428b1853e5ec29d35c84a218ec5170efc7621b58"], - ["0x735b2f2c662ebedffa94027a7196f0559f7f18a4"], - ["0x8225d72f2c39f3729d7f3fc03c6aa8731eaeef48"] -] -``` + + -
+ 1. Replace `query.json`. + + ```json filename="query.json" copy + [ + { + "address": "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", + "query": "SELECT taskCreator FROM app__Creator GROUP BY taskCreator" + } + ] + ``` + + 1. Run the query. + + ```sh copy + curl https://indexer.mud.garnetchain.com/q --compressed \ + -H 'Accept-Encoding: gzip' \ + -H 'Content-Type: application/json' \ + -d @query.json | jq + ``` + + + + -### Metadata +## Metadata You can use the `/tables` path to get the list of either all tables, or all tables that match a string. As per the SQL standard, the wildcard is `%`. @@ -521,7 +1081,7 @@ You can query the SQL API from [Typescript](https://www.typescriptlang.org/) wit 1. Replace `src/main.ts` with this file. ```ts filename="main.ts" - import { fetchRecordsSql, selectFrom } from "@latticexyz/store-sync/internal"; + import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; import { defineStore } from "@latticexyz/store"; const config = defineStore({ @@ -552,7 +1112,7 @@ You can query the SQL API from [Typescript](https://www.typescriptlang.org/) wit limit: 2, }); - const queryResult = await fetchRecordsSql({ + const queryResult = await fetchRecords({ indexerUrl: "https://indexer.mud.garnetchain.com/q", storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", queries: [queryUncompleted], @@ -574,7 +1134,7 @@ You can query the SQL API from [Typescript](https://www.typescriptlang.org/) wit Explanation ```ts -import { fetchRecordsSql, selectFrom } from "@latticexyz/store-sync/internal"; +import { fetchRecords, selectFrom } from "@latticexyz/store-sync/internal"; import { defineStore } from "@latticexyz/store"; ``` @@ -605,7 +1165,7 @@ The queries supported by `selectFrom` are a subset of those the SQL API supports The results come from a single table, and only `WHERE` and `LIMIT` clauses are supported. ```typescript -const queryResult = await fetchRecordsSql({ +const queryResult = await fetchRecords({ indexrUrl: "https://indexer.mud.garnetchain.com/q", storeAddress: "0x95F5d049B014114E2fEeB5d8d994358Ce4FFd06e", queries: [queryUncompleted],