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

Add "or" operator to CrudFilter #1598

Merged
merged 29 commits into from
Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0dd0f74
update CrudFilter type and add "or" operator to CrudOperators
salihozdemir Feb 28, 2022
b0b7350
add "or" operator support to strapi-v4 genereteFilters
salihozdemir Feb 28, 2022
4c9892c
update CrudFilter with discriminated unions
salihozdemir Mar 1, 2022
af4b1c7
add "or" operator support to strapi-v4 data provider
salihozdemir Mar 1, 2022
35be022
update airtable data provider
salihozdemir Mar 2, 2022
54d905f
update altogic data provider
salihozdemir Mar 2, 2022
0735a9c
update appwrite data provider
salihozdemir Mar 2, 2022
ee7ae3a
refactoring stringify and parse functions
salihozdemir Mar 2, 2022
e6e9a5d
fix strapi-v4 in operator bug
salihozdemir Mar 2, 2022
1d0116a
fix getDefaultFilter and mapAntdFilterToCrudFilter functions in antd …
salihozdemir Mar 2, 2022
2001954
add or operator support to strapi data provider
salihozdemir Mar 3, 2022
4ddbda1
add nullish control to strapi graphql example
salihozdemir Mar 3, 2022
234343a
add or operator support to strapi graphql data provider
salihozdemir Mar 3, 2022
606bba2
add or operator support to supabase data provider
salihozdemir Mar 3, 2022
a793ca3
update simple rest data provider
salihozdemir Mar 3, 2022
0d7c8b2
Merge branch 'master' of https://github.com/pankod/refine into or-con…
salihozdemir Mar 3, 2022
2475302
update graphql data provider
salihozdemir Mar 3, 2022
0421d1c
add or operator support to hasura data provider
salihozdemir Mar 3, 2022
06f71c9
add or operator support to nhost data provider
salihozdemir Mar 3, 2022
cf372cc
add or operator support to nestjsx crud data provider
salihozdemir Mar 3, 2022
80d4d35
add types for parsed sorters and parsed filters
salihozdemir Mar 3, 2022
b2d0bbb
Merge branch 'master' of https://github.com/pankod/refine into or-con…
salihozdemir Mar 4, 2022
df36a4e
export logical filter and conditional filter types
salihozdemir Mar 4, 2022
ea09e53
fix react table package
salihozdemir Mar 4, 2022
b3facea
fix graphql package tests
salihozdemir Mar 4, 2022
cdddc76
remove some supabase tests beacause these test are supported, now
salihozdemir Mar 4, 2022
98215aa
add handling filters doc
salihozdemir Mar 7, 2022
8815276
add handle in a data provider section
salihozdemir Mar 7, 2022
d90406a
fix conditional filter example
salihozdemir Mar 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
id: handling-filters
title: Handling Filters
---

**refine** expects an array of type `CrudFilters` to filter results based on some field’s values. So you can use more than one filter. Even the `or` operator can be used to combine multiple filters.

## `CrudFilters`

`CrudFilters` is an array of objects with the following properties:

```ts
// Supported operators:
type CrudOperators =
| "eq"
| "ne"
| "lt"
| "gt"
| "lte"
| "gte"
| "in"
| "nin"
| "contains"
| "ncontains"
| "containss"
| "ncontainss"
| "between"
| "nbetween"
| "null"
| "nnull"
| "or";

// Supported filter types:
type LogicalFilter = {
field: string;
operator: Exclude<CrudOperators, "or">;
value: any;
};

type ConditionalFilter = {
operator: "or";
value: LogicalFilter[];
};

type CrudFilter = LogicalFilter | ConditionalFilter;
//highlight-next-line
type CrudFilters = CrudFilter[];
```

## `LogicalFilters`

`LogicalFilter` works with `AND` logic. For example, if you want to filter by `name` field and `age` field, you can use the following filter:

```ts
const filter = [
{
field: "name",
operator: "eq",
value: "John",
},
{
field: "age",
operator: "lt",
value: 30,
},
];
```

Here the query will look like: `"name" = "John" AND "age" < 30`

## `ConditionalFilters`

`ConditionalFilter` works only with `or` operator and expects an array of `LogicalFilter` objects in the `value` property. For example, if you want to filter by `age` field or `createdAt` field, you can use the following filter:

```ts
const filter = [
{
operator: "or",
value: [
{
field: "age",
operator: "eq",
value: 30,
},
{
field: "createdAt",
operator: "gte",
value: "2018-01-01",
},
],
},
];
```

Here the query will look like: `"age" = 30 OR "createdAt" <= "2018-01-01"`

## Combining Filters

You can group multiple parameters in the same query using the logical filters or the conditional filters operators to filter results based on more than one criteria at the same time. This allows you to create more complex queries.

Example query: Find posts with 2 possible dates & a specific status

```ts
filter = [
{
operator: "or",
value: [
{
field: "createdAt",
operator: "eq",
value: "2022-01-01",
},
{
field: "createdAt",
operator: "eq",
value: "2022-01-02",
},
],
},
{
operator: "eq",
field: "status",
value: "published",
},
];
```

Here the query will look like:
`"status" == "published" AND ("createdAt" == "2022-01-01" OR "createdAt" == "2022-01-02")`

## Handle filters in a data provider

```tsx title="dataProvider.ts"
import { DataProvider } from "@pankod/refine-core";

const dataProvider = (): DataProvider => ({
getList: async ({ resource, pagination, filters, sort }) => {
if (filters) {
filters.map((filter) => {
if (filter.operator !== "or") {
// Handle your logical filters here
// console.log(typeof filter); // LogicalFilter
} else {
// Handle your conditional filters here
// console.log(typeof filter); // ConditionalFilter
}
});
}
},
});
```

:::tip
Data providers that support `or` and `and` filtering logic are as follows:

- [NestJS CRUD](https://github.com/pankod/refine/tree/master/packages/nestjsx-crud)
- [Strapi](https://github.com/pankod/refine/tree/master/packages/strapi) - [Strapi v4](https://github.com/pankod/refine/tree/master/packages/strapi-v4)
- [Strapi GraphQL](https://github.com/pankod/refine/tree/master/packages/strapi-graphql)
- [Supabase](https://github.com/pankod/refine/tree/master/packages/supabase)
- [Hasura](https://github.com/pankod/refine/tree/master/packages/hasura)
- [Nhost](https://github.com/pankod/refine/tree/master/packages/nhost)

:::
1 change: 1 addition & 0 deletions documentation/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ module.exports = {
type: "category",
label: "Data Provider",
items: [
"guides-and-concepts/data-provider/handling-filters",
"guides-and-concepts/data-provider/graphql",
"guides-and-concepts/data-provider/strapi-v4",
"guides-and-concepts/data-provider/appwrite",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export interface IPost {
id: string;
title: string;
content: string;
category: ICategory;
category?: ICategory;
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const PostEdit: React.FC<IResourceComponentsProps> = () => {
const postData = queryResult?.data?.data;
const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
defaultValue: postData?.category.id,
defaultValue: postData?.category?.id,
metaData: {
fields: ["id", "title"],
},
Expand All @@ -68,7 +68,7 @@ export const PostEdit: React.FC<IResourceComponentsProps> = () => {
onFinish={(values) =>
formProps.onFinish?.({
...values,
category: values.category.id,
category: values.category?.id,
} as any)
}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const PostList: React.FC<IResourceComponentsProps> = () => {
id: item.id,
title: item.title,
content: item.content,
category: item.category.id,
category: item.category?.id,
};
},
metaData: {
Expand Down Expand Up @@ -113,7 +113,7 @@ export const PostList: React.FC<IResourceComponentsProps> = () => {
/>
</FilterDropdown>
)}
render={(_, record) => record.category.title}
render={(_, record) => record.category?.title}
/>
<Table.Column<IPost>
title="Actions"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const PostShow: React.FC<IResourceComponentsProps> = () => {
<Text>{record?.title}</Text>

<Title level={5}>Category</Title>
<Text>{record?.category.title}</Text>
<Text>{record?.category?.title}</Text>

<Title level={5}>Content</Title>
<MarkdownField value={record?.content} />
Expand Down
11 changes: 6 additions & 5 deletions packages/airtable/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ const simpleOperators: Partial<Record<CrudOperators, string>> = {

const generateFilter = (filters?: CrudFilters): string | undefined => {
if (filters) {
const parsedFilter = filters.map(
({ field, operator, value }): Formula => {
const parsedFilter = filters.map((filter): Formula => {
if (filter.operator !== "or") {
const { field, operator, value } = filter;
if (Object.keys(simpleOperators).includes(operator)) {
const mappedOperator =
simpleOperators[
Expand Down Expand Up @@ -70,10 +71,10 @@ const generateFilter = (filters?: CrudFilters): string | undefined => {

return [value ? "=" : "!=", { field }, ["BLANK"]];
}
}

throw Error(`Operator ${operator} is not supported`);
},
);
throw Error(`Operator ${filter.operator} is not supported`);
});

return compile(["AND", ...parsedFilter]);
}
Expand Down
38 changes: 21 additions & 17 deletions packages/altogic/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,27 @@ const generateSort = (sort?: CrudSorting) => {
const generateFilter = (filters?: CrudFilters) => {
const queryFilters: string[] = [];
if (filters) {
filters.map(({ field, operator, value }) => {
const mappedOperator = mapOperator(operator);

switch (mappedOperator) {
case "IN":
case "NIN":
queryFilters.push(
`${mappedOperator}(${JSON.stringify(
value,
)}, this.${field})`,
);
break;

default:
queryFilters.push(
`this.${field} ${mappedOperator} "${value}"`,
);
filters.map((filter) => {
const mappedOperator = mapOperator(filter.operator);

if (filter.operator !== "or") {
const { field, value } = filter;

switch (mappedOperator) {
case "IN":
case "NIN":
queryFilters.push(
`${mappedOperator}(${JSON.stringify(
value,
)}, this.${field})`,
);
break;

default:
queryFilters.push(
`this.${field} ${mappedOperator} "${value}"`,
);
}
}
});
}
Expand Down
28 changes: 18 additions & 10 deletions packages/antd/src/definitions/table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
CrudOperators,
CrudSorting,
CrudFilter,
LogicalFilter,
} from "@pankod/refine-core";
import { SortOrder, SorterResult } from "antd/lib/table/interface";

Expand All @@ -28,10 +29,13 @@ export const getDefaultFilter = (
filters?: CrudFilters,
operatorType: CrudOperators = "eq",
): CrudFilter["value"] | undefined => {
const filter = filters?.find(
({ field, operator }) =>
field === columnName && operator === operatorType,
);
const filter = filters?.find((filter) => {
if (filter.operator !== "or") {
const { operator, field } = filter;
return field === columnName && operator === operatorType;
}
return undefined;
});

if (filter) {
return filter.value || [];
Expand Down Expand Up @@ -84,13 +88,17 @@ export const mapAntdFilterToCrudFilter = (

Object.keys(tableFilters).map((field) => {
const value = tableFilters[field];
const operator = prevFilters.find((p) => p.field === field)?.operator;
const operator = prevFilters
.filter((i) => i.operator !== "or")
.find((p: LogicalFilter) => p.field === field)?.operator;

crudFilters.push({
field,
operator: operator ?? (Array.isArray(value) ? "in" : "eq"),
value,
});
if (operator !== "or") {
crudFilters.push({
field,
operator: operator ?? (Array.isArray(value) ? "in" : "eq"),
value,
});
}
});

return crudFilters;
Expand Down
8 changes: 6 additions & 2 deletions packages/appwrite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const operators = {
between: undefined,
nbetween: undefined,
nnull: undefined,
or: undefined,
};

const appwriteEventToRefineEvent = {
Expand Down Expand Up @@ -66,13 +67,16 @@ export const getAppwriteFilters: GetAppwriteFiltersType = (filters) => {

for (const filter of filters) {
const operator = operators[filter.operator];
const filterField = filter.field === "id" ? "$id" : filter.field;

if (!operator) {
throw new Error(`Operator ${filter.operator} is not supported`);
}

appwriteFilters.push(`${filterField}${operator}${filter.value}`);
if (filter.operator !== "or") {
const filterField = filter.field === "id" ? "$id" : filter.field;

appwriteFilters.push(`${filterField}${operator}${filter.value}`);
}
}

return appwriteFilters;
Expand Down
13 changes: 10 additions & 3 deletions packages/core/src/contexts/data/IDataContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,21 @@ export type CrudOperators =
| "between"
| "nbetween"
| "null"
| "nnull";
| "nnull"
| "or";

export type CrudFilter = {
export type LogicalFilter = {
field: string;
operator: CrudOperators;
operator: Exclude<CrudOperators, "or">;
value: any;
};

export type ConditionalFilter = {
operator: "or";
value: LogicalFilter[];
};

export type CrudFilter = LogicalFilter | ConditionalFilter;
export type CrudSort = {
field: string;
order: "asc" | "desc";
Expand Down