Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

Commit

Permalink
enhancement: add filtering, sorting, and pagination to GraphQL queries (
Browse files Browse the repository at this point in the history
#763)

* enhancement: add filtering to GraphQL API queries (#721)

Add filtering functionality to GraphQL API; add E2E tests

* enhancement: add sorting and offset-based pagination to GraphQL API queries (#755)

* Add comments; rename to clearer variables

* Add sorting and offset-based pagination

* enhancement: add `PageInfo` object to paginated GraphQL responses (#761)

* Add PageInfo object to queries with a limit

* Adjust tests

* Change API response to snake_case

* docs: add docs for GraphQL filtering and pagination (#768)

* Add docs for GraphQL filtering and pagination

* Move QueryResponse to models module

* Address rvmelkonian feedback

* Address ra0x3 feedback

* rebase origin/master

* Add fuel-indexer-graphql(-parser) packages

* Fix index plugins failure

* Clippy fixes

---------

Co-authored-by: Alexander <[email protected]>
Co-authored-by: Rashad Alston <[email protected]>
  • Loading branch information
3 people authored Apr 26, 2023
1 parent 3634a8e commit a8cdc0a
Show file tree
Hide file tree
Showing 117 changed files with 8,210 additions and 476 deletions.
73 changes: 56 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ members = [
"packages/fuel-indexer-database",
"packages/fuel-indexer-database/database-types",
"packages/fuel-indexer-database/postgres",
"packages/fuel-indexer-graphql",
"packages/fuel-indexer-graphql/parser",
"packages/fuel-indexer-lib",
"packages/fuel-indexer-macros",
"packages/fuel-indexer-metrics",
Expand Down Expand Up @@ -49,6 +51,8 @@ fuel-indexer = { version = "0.10.0", path = "./packages/fuel-indexer" }
fuel-indexer-api-server = { version = "0.10.0", path = "./packages/fuel-indexer-api-server" }
fuel-indexer-database = { version = "0.10.0", path = "./packages/fuel-indexer-database" }
fuel-indexer-database-types = { version = "0.10.0", path = "./packages/fuel-indexer-database/database-types" }
fuel-indexer-graphql = { version = "0.10.0", path = "./packages/fuel-indexer-graphql" }
fuel-indexer-graphql-parser = { version = "0.10.0", path = "./packages/fuel-indexer-graphql/parser" }
fuel-indexer-lib = { version = "0.10.0", path = "./packages/fuel-indexer-lib" }
fuel-indexer-macros = { version = "0.10.0", path = "./packages/fuel-indexer-macros", default-features = false }
fuel-indexer-metrics = { version = "0.10.0", path = "./packages/fuel-indexer-metrics" }
Expand Down
4 changes: 4 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
- [Directives](./reference-guide/components/graphql/directives.md)
- [API Server](./reference-guide/components/graphql/api-server.md)
- [Playground](./reference-guide/components/graphql/playground.md)
- [Queries](./reference-guide/components/graphql/queries/index.md)
- [Search and Filtering](./reference-guide/components/graphql/queries/search-filtering.md)
- [Pagination](./reference-guide/components/graphql/queries/pagination.md)
- [A Full Example](./reference-guide/components/graphql/queries/full-example.md)
- [Database](./reference-guide/components/database/index.md)
- [Foreign Keys](./reference-guide/components/database/foreign-keys.md)
- [ID Types](./reference-guide/components/database/ids.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# A Full Example

Finally, let's combine nested entities, filtering, and pagination into one complete example.

Sticking with the same block explorer example, let's say that we are looking for a particular transaction and its containing block, but we don't remember either of the hashes. All we know is that the total value of the transaction is greater than zero, it was sometime after the start of the `beta-3` testnet, and it was included as part of the first fifty blocks. Additionally, we don't want to parse through all the results at once, so we only want to look at two records at a time. Finally, we think that it may have been on the more recent side, so we want to check them in reverse chronological order.

Putting all of that together, we get the following query:

```graphql
query {
transactions: tx(
order: { desc: timestamp },
filter: { value: { gt: 0 } },
first: 2,
offset: 0
) {
id
hash
timestamp
value
block (
filter: {
height: { between: { min: 0, max: 50 } },
and: {
timestamp: { gt: 1678410000 }
}
}
) {
id
hash
height
timestamp
}
}
}
```

The Fuel indexer's GraphQL API allows you to add filters on multiple entity fields and even nested entities! In the query above, we're asking for the two most recent transactions with a value greater than zero. Also, we're applying two filters to the nested `block` entity by using the `and` operator in order to help us narrow down the set of results.

The response returns the results in the expected format and includes additional information that informs us about how many total results satisy the criteria.

```json
{
"data": {
"page_info": {
"has_next_page": true,
"limit": 2,
"offset": 0,
"pages": 2,
"total_count": 4
},
"transactions": [
{
"block": {
"hash": "f40297895086e66c0947c213dd29e90f596b860d10316ab806064608dd2580cd",
"height": 45,
"id": 7306026486395921000,
"timestamp": 1678486898
},
"hash": "85acfa181ebfa3b48c10d3181217918dd377b875d07dabc72d6d1081e4c52713",
"id": 3919319574514776000,
"timestamp": 1678486898,
"value": 10000000000
},
{
"block": {
"hash": "e3e0860a358c0d044669748cffff82b4b0073baaca53a128ddc8ce3757ae3988",
"height": 41,
"id": 7018409465212200000,
"timestamp": 1678486633
},
"hash": "42f3fd7ffa073975a0eca993044a867d8c87a8d39f5a88032a3b9aba213f6102",
"id": 7364622549171910000,
"timestamp": 1678486633,
"value": 10000000000
}
]
}
}
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Queries

Once data has been persisted into your storage backend, you can retrieve it by querying the [GraphQL API server](./api-server.md). By default, the API server can be reached at `http://localhost:29987/api/graph/<namespace>/<identifier>`, where `<namespace>` and `<identifier>` are the values for the respective fields in your indexer's manifest. If you've changed the `GRAPHQL_API_HOST` or `GRAPHQL_API_PORT` values of your configuration, then you'll need to adjust the URL accordingly.
Once data has been persisted into your storage backend, you can retrieve it by querying the [GraphQL API server](./api-server.md). By default, the API server can be reached at `http://localhost:29987/api/graph/:namespace/:identifier`, where `:namespace` and `:identifier` are the values for the respective fields in your indexer's manifest. If you've changed the `GRAPHQL_API_HOST` or `GRAPHQL_API_PORT` values of your configuration, then you'll need to adjust the URL accordingly.

## Basic Query

Expand All @@ -19,16 +19,19 @@ query {

The `entity` field corresponds to the name of an entity defined in your [schema](./schema.md) and the sub-fields are the fields defined on that entity type; entities and fields are stored in the database using the names defined in the schema, so make sure that your query uses those same names as well.

Let's refer back to the [block explorer](../../../examples/block-explorer.md) example for an illustration. After the block data has been indexed, we can retrieve information about the blocks by sending a query to the graph endpoint for our indexer.
Let's refer back to the [block explorer](../../../examples/block-explorer.md) example for an illustration. After the block data has been indexed, we can retrieve information about the blocks by querying for information on the indexer's playground; you can get to the playground by starting the block explorer example using the instructions on the page and navigating to `http://localhost:29987/api/graph/fuel_examples/explorer_indexer`.

```txt
curl -X POST http://localhost:29987/api/graph/fuel_examples/explorer_index \
-H 'content-type: application/json' \
-d '{"query": "query { block { id height timestamp }}", "params": "b"}' \
| json_pp
query {
block {
id
height
timestamp
}
}
```

In the above snippet, you can see that we're requesting the ID, height, and timestamp for each block stored in the backend, and we're doing so by sending a `POST` request with a JSON payload. You can also see that the query is set as the value for the `query` key in the payload. If successful, the API server will return a response similar to the following:
We're requesting the ID, height, and timestamp for each block stored in the backend. If successful, the API server will return a response similar to the following:

```json
[
Expand Down Expand Up @@ -137,16 +140,7 @@ query {
}
```

Let's assume that we've created an indexer for this data and indexed data has been stored in the database. Now we'll send a request to the API server in the same way that we did for the basic query example:

```txt
curl -X POST http://localhost:29987/api/graph/fuel_examples/nested_query_index \
-H 'content-type: application/json' \
-d '{"query": "query { character { name book { title library { name city { name } } } } }", "params": "b"}' \
| json_pp
```

And we receive the following response:
Let's assume that we've created an indexer for this data and the indexed data has been stored in the database. If we send the query, we'll get the following response:

```json
[
Expand Down
Loading

0 comments on commit a8cdc0a

Please sign in to comment.