Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion dbquery/pagination/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ To run these examples,

This example uses a demo database filled with mock data, you can inspect the `config.yaml` file to find the credentials for this database.


## GraphQL pagination

StepZen supports pagination through the standard [GraphQL Cursor Connection Specification](https://relay.dev/graphql/connections.htm).
Expand Down Expand Up @@ -63,4 +62,51 @@ Fetch the next five, the value of `after` is taken from the `endCursor` field of
stepzen request -f operations.graphql --operation-name=Customers --var first=5 --var after="eyJjIjoiTDpRdWVyeTpjdXN0b21lcnMiLCJvIjo0fQ=="
```

## GraphQL filtering

Filtering using a "MongoDB" inspired style for GraphQL is supported with `@dbquery` and is typically
used with pagination, though it can be used alone. [Reference](https://www.ibm.com/docs/en/api-connect-graphql/saas?topic=directives-directive-dbquery#filtering__title__1)

The field `Query.lookup` in [`filter.graphql`](./filter.graphql) is similar to `Query.customers` but adds a `filter` argument.

This filter value would return all names that start with "S":

```json
{ "name": { "like": "S%" } }
```

This returns two customers using an OR clause:

```json
{
"or": {
"name": { "eq": "John Doe" },
"email": { "eq": "jane.smith@example.com" }
}
}
```

### Examples

With no filter all customers are returned (subject to pagination)

```
stepzen request -f operations.graphql --operation-name=Lookup
```

With a filter then only matching customers are returned.

```
stepzen request -f operations.graphql --operation-name=Lookup --var 'f={ "name": { "like": "S%" } }'
```

```
stepzen request -f operations.graphql --operation-name=Lookup --var first=5 --var 'f={
"or": {
"name": { "eq": "John Doe" },
"email": { "eq": "jane.smith@example.com" }
}
}'
```

Also note that `totalEdges` returns the number of customers (edges) in the connection subject to the filter.
37 changes: 37 additions & 0 deletions dbquery/pagination/filter.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This shows how filtering is supported with @dbquery

input CustomerFilter {
id: ID
name: StringFilter
email: StringFilter
or: CustomerFilter
}

input StringFilter {
eq: String
ne: String
like: String
ilike: String
gt: String
le: String
}

extend type Query {
"""
`lookup` pages through mock `Customer` objects in a demo PostgreSQL database.
The data is exposed using standard GraphQL pagination using the connection model.
The filter argument provides a standard approach to filtering regardless of
the backend and in this case is dynamically translated to SQL predicates in the `WHERE` clause.
"""
lookup(
first: Int! = 10
after: String! = ""
filter: CustomerFilter
): CustomerConnection
@dbquery(
type: "postgresql"
table: "customer"
schema: "public"
configuration: "postgresql_config"
)
}
6 changes: 5 additions & 1 deletion dbquery/pagination/index.graphql
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
schema @sdl(files: ["api.graphql"]) {
schema @sdl(files: ["api.graphql", "filter.graphql"]) {
query: Query
}

# this validates that the executable document is valid for the schema.
# If the schema and document become out of sync then deployment will fail.
extend schema @sdl(executables: { document: "operations.graphql" })
18 changes: 18 additions & 0 deletions dbquery/pagination/operations.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,21 @@ query Customers($first: Int!, $after: String = "") {
totalEdges
}
}

# Lookup customers with filtering.
query Lookup($first: Int, $after: String = "", $f: CustomerFilter) {
lookup(first: $first, after: $after, filter: $f) {
edges {
node {
id
name
email
}
}
pageInfo {
endCursor
hasNextPage
}
totalEdges
}
}
91 changes: 65 additions & 26 deletions dbquery/pagination/tests/Test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,38 @@ const requestsFile = path.join(path.dirname(__dirname), "operations.graphql");
const requests = fs.readFileSync(requestsFile, "utf8").toString();

describe(testDescription, function () {
const CURSOR = "eyJjIjoiTDpRdWVyeTpjdXN0b21lcnMiLCJvIjoxfQ=="
const CURSOR = "eyJjIjoiTDpRdWVyeTpjdXN0b21lcnMiLCJvIjoxfQ==";

const tests = [
{
label: "first set of results",
query: requests,
operationName: "Customers",
variables: {
first: 2
variables: {
first: 2,
},
expected: {
customers: {
edges: [
{
node: {
id: "1",
name: "Lucas Bill "
}
{
node: {
id: "1",
name: "Lucas Bill ",
},
},
{
node: {
id: "2",
name: "Mandy Jones ",
},
},
{
node: {
id: "2",
name: "Mandy Jones "
}
}
],
pageInfo: {
hasNextPage: true,
endCursor: CURSOR
endCursor: CURSOR,
},
totalEdges: 10
}
totalEdges: 10,
},
},
},
{
Expand All @@ -52,30 +52,69 @@ describe(testDescription, function () {
operationName: "Customers",
variables: {
first: 2,
after: CURSOR
after: CURSOR,
},
expected: {
customers: {
edges: [
{
node: {
id: "3",
name: "Salim Ali "
}
name: "Salim Ali ",
},
},
{
node: {
id: "4",
name: "Jane Xiu "
}
}
name: "Jane Xiu ",
},
},
],
pageInfo: {
endCursor: "eyJjIjoiTDpRdWVyeTpjdXN0b21lcnMiLCJvIjozfQ==",
hasNextPage: true
hasNextPage: true,
},
totalEdges: 10,
},
},
},
{
label: "lookup-filter",
query: requests,
operationName: "Lookup",
variables: {
first: 2,
f: {
or: {
name: { eq: "John Doe" },
email: { eq: "jane.smith@example.com" },
},
},
},
expected: {
customers: {
edges: [
{
node: {
id: "5",
name: "John Doe ",
email: "john.doe@example.com ",
},
},
{
node: {
id: "6",
name: "Jane Smith ",
email: "jane.smith@example.com ",
},
},
],
pageInfo: {
hasNextPage: true,
endCursor: "eyJjIjoiTDpRdWVyeTpsb29rdXAiLCJvIjoxfQ==",
},
totalEdges: 10
}
totalEdges: 2,
},
},
},
];
Expand Down
Loading