Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add filterQueryOverride to GraphQL Service #1214

Merged
merged 2 commits into from
Jun 8, 2024
Merged
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
21 changes: 21 additions & 0 deletions docs/backend-services/graphql/GraphQL-Filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
- [filterBy](#filterby)
- [Complex Objects](#complex-objects)
- [Extra Query Arguments](#extra-query-arguments)
- [Override the filter query](#override-the-filter-query)

### Introduction
The implementation of a GraphQL Service requires a certain structure to follow for `Slickgrid-Universal` to work correctly (it will fail if your GraphQL Schema is any different than what is shown below).
Expand Down Expand Up @@ -134,3 +135,23 @@ this.gridOptions = {
}
}
```

### Override the filter query

Column filters may have a `Custom` Operator, that acts as a placeholder for you to define your own logic. To do so, the easiest way is to provide the `filterQueryOverride` callback in the GraphQL Options. This method will be called with `BackendServiceFilterQueryOverrideArgs` to let you decide dynamically on how the filter should be assembled.

E.g. you could listen for a specific column and the active `OperatorType.custom` in order to switch the filter to an SQL LIKE search in GraphQL:

> **Note** technically speaking GraphQL isn't a database query language like SQL, it's an application query language. Depending on your configuration, your GraphQL Server might already support regex querying (e.g. Hasura [_regex](https://hasura.io/docs/latest/queries/postgres/filters/text-search-operators/#_regex)) or you could add your own implementation (e.g. see this SO: https://stackoverflow.com/a/37981802/1212166). Just make sure that whatever custom operator that you want to use, is already included in your GraphQL Schema.
```ts
backendServiceApi: {
options: {
filterQueryOverride: ({ fieldName, columnDef, columnFilterOperator, searchValue }) => {
if (columnFilterOperator === OperatorType.custom && columnDef?.id === 'name') {
// the `operator` is a string, make sure to implement this new operator in your GraphQL Schema
return { field: fieldName, operator: 'Like', value: searchValue };
}
},
}
}
```
29 changes: 27 additions & 2 deletions packages/demo/src/examples/slickgrid/example6.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export class Example6 {
<li>The (*) can be used as startsWith (ex.: "abc*" => startsWith "abc") / endsWith (ex.: "*xyz" => endsWith "xyz")</li>
<li>The other operators can be used on column type number for example: ">=100" (greater or equal than 100)</li>
</ul>
<li>You can also preload a grid with certain "presets" like Filters / Sorters / Pagination <a href="https://ghiscoding.gitbook.io/aurelia-slickgrid/grid-functionalities/grid-state-preset" target="_blank">Wiki - Grid Preset</a>
<li>You can also preload a grid with certain "presets" like Filters / Sorters / Pagination <a href="https://ghiscoding.gitbook.io/aurelia-slickgrid/grid-functionalities/grid-state-preset" target="_blank">Wiki - Grid Preset</a></li>
<li>Also note that the column Name has a filter with a custom %% operator that behaves like an SQL LIKE operator supporting % wildcards.</li>
<li>Depending on your configuration, your GraphQL Server might already support regex querying (e.g. Hasura <a href="https://hasura.io/docs/latest/queries/postgres/filters/text-search-operators/#_regex" target="_blank">_regex</a>)
or you could add your own implementation (e.g. see this SO <a href="https://stackoverflow.com/a/37981802/1212166">Question</a>).</li>
</ul>
`;
isWithCursor = false;
Expand Down Expand Up @@ -77,7 +80,15 @@ export class Example6 {
sortable: true,
filterable: true,
filter: {
model: Filters.compoundInput
model: Filters.compoundInput,
compoundOperatorList: [
{ operator: '', desc: 'Contains' },
{ operator: '<>', desc: 'Not Contains' },
{ operator: '=', desc: 'Equals' },
{ operator: '!=', desc: 'Not equal to' },
{ operator: 'a*', desc: 'Starts With' },
{ operator: 'Custom', desc: 'SQL Like' },
],
}
},
{
Expand Down Expand Up @@ -137,6 +148,10 @@ export class Example6 {
i18n: this.i18n,
gridHeight: 200,
gridWidth: 900,
compoundOperatorAltTexts: {
// where '=' is any of the `OperatorString` type shown above
text: { 'Custom': { operatorAlt: '%%', descAlt: 'SQL Like' } },
},
gridMenu: {
resizeOnShowHeaderRow: true,
},
Expand Down Expand Up @@ -180,6 +195,16 @@ export class Example6 {
field: 'userId',
value: 123
}],
filterQueryOverride: ({ fieldName, columnDef, columnFilterOperator, searchValue }) => {
if (columnFilterOperator === OperatorType.custom && columnDef?.id === 'name') {
// technically speaking GraphQL isn't a database query language like SQL, it's an application query language.
// What that means is that GraphQL won't let you write arbitrary queries out of the box.
// It will only support the types of queries defined in your GraphQL schema.
// see this SO: https://stackoverflow.com/a/37981802/1212166
return { field: fieldName, operator: 'Like', value: searchValue };
}
return;
},
useCursor: this.isWithCursor, // sets pagination strategy, if true requires a call to setPageInfo() when graphql call returns
// when dealing with complex objects, we want to keep our field name with double quotes
// example with gender: query { users (orderBy:[{field:"gender",direction:ASC}]) {}
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"noEmit": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noImplicitReturns": false,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
Expand Down
26 changes: 26 additions & 0 deletions test/cypress/e2e/example06.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { removeWhitespaces } from '../plugins/utilities';

const presetLowestDay = format(addDay(new Date(), -2), 'YYYY-MM-DD');
const presetHighestDay = format(addDay(new Date(), 20), 'YYYY-MM-DD');
function removeSpaces(textS) {
return `${textS}`.replace(/\s+/g, '');
}

describe('Example 6 - GraphQL Grid', () => {
it('should display Example title', () => {
Expand Down Expand Up @@ -295,6 +298,29 @@ describe('Example 6 - GraphQL Grid', () => {
});
});

it('should perform filterQueryOverride when operator "%%" is selected', () => {
cy.get('.search-filter.filter-name select').find('option').last().then((element) => {
cy.get('.search-filter.filter-name select').select(element.val());
});

cy.get('.search-filter.filter-name')
.find('input')
.clear()
.type('Jo%yn%er');

// wait for the query to finish
cy.get('[data-test=status]').should('contain', 'finished');

cy.get('[data-test=graphql-query-result]')
.should(($span) => {
const text = removeSpaces($span.text()); // remove all white spaces
expect(text).to.eq(removeSpaces(`query { users (first:30,offset:0,
orderBy:[{field:"name",direction:ASC}],
filterBy:[{field:"name",operator:Like,value:"Jo%yn%er"}],
locale:"en",userId:123) { totalCount, nodes { id,name,gender,company,billing{address{street,zip}},finish } } }`));
});
});

it('should click on Set Dynamic Filter and expect query and filters to be changed', () => {
cy.get('[data-test=set-dynamic-filter]')
.click();
Expand Down