refine icon indicating copy to clipboard operation
refine copied to clipboard

[FEAT] Allow StrapiV4 CRUDFilters to do deep filter

Open albcunha opened this issue 3 years ago • 6 comments

Is your feature request related to a problem? Please describe. On Strapi, you can use objects to make deep filters (https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest/filtering-locale-publication.html#deep-filtering) Example:

filters: {
    chef: {
      restaurants: {
        stars: {
          $eq: 5,
        },
      },
    },
},

GET /api/restaurants?filters[chef][restaurants][stars][$eq]=5

Refine, only allows arrays, with logical filters or conditional filters (https://refine.dev/docs/guides-and-concepts/data-provider/handling-filters/): Example:

const filter = [
    {
        field: "name",
        operator: "eq",
        value: "John",
    },

If I try to use a relation field as a filter, strapi will throw an error.

Describe the solution you'd like I´d like to use deep filters so I can filter some relational tables. On my real case, I´m doing a todo list for each project. I also need to filter this todo list based on its procedure, on another component, and I need to mutate it too. Doing this filtering makes easier on the code logic.

Describe alternatives you've considered I can cheat the URL constructor doing this:

const filter = [
    {
        field: "chef[restaurants][stars",
        operator: "eq",
        value: "John",
    },

It converts to this: /api/restaurants?filters[chef][restaurants][stars][$eq]=5 It works!

albcunha avatar Jul 09 '22 03:07 albcunha

Hello! Thanks for the issue. To be honest I'm a bit skeptical about this structure and how we can support this. filters are handled inside @pankod/refine-core and the shape of filters are meant to be generic and provider agnostic.

I'm recalling a similar difference in @pankod/refine-supabase but it was in sort side. We've added an ability to supabase provider in order to transform field values with dot notation to the format supabase support.

If we're going to introduce a change in the core, we need to make it in a way that is aligned with other providers. Without having any change in the core, all I could think of right now is to export some helpers/functions to allow user to write filters in strapi format and let the helper transform this into refine format. This might be easier to write for users that are already familiar with strapi and remove the need of any change in the core.

I'm talking about something like:

import { strapiToRefineFilters } from "@pankod/refine-strapi-v4";

const filters = strapiToRefineFilters({
    chef: {
      restaurants: {
        stars: {
          $eq: 5,
        },
      },
    },
});

eventually will transform into this (currently supported way/workaround)

{
    field: "chef[restaurants][stars]",
    operator: "eq",
    value: 5,
}

My point is to keep the core clean from provider specific enhancements 🤔

@albcunha I'd love to hear your comments to my suggestion above and I'll be waiting for your solution ideas. 🙏

aliemir avatar Jul 12 '22 21:07 aliemir

That would work, but as you can deepnest populate, you would expect to also be able to deep nest filter.

Maybe we could change dataProvider? refine/Strapi-V4 constructs dataprovider with strings (https://github.com/pankod/refine/blob/next/packages/strapi-v4/src/dataProvider.ts) On Strapi, they throw the object on qs and retrieve the query filter string. Check their docs on JavaScript query (built with the qs library): https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest/filtering-locale-publication.html#complex-filtering Maybe we can map refine.dev object and create a strapi.io qs object that strapi understands? My code probably wont handle edge cases, but maybe something like this:

const createStrapiQs = (filter) => {
    let finalJson: any = {};
    function traverse(o) {
      for (var i in o) {
        if (o[i] !== null && typeof o[i] === "object") {
          if ('field' in o[i] && 'operator' in o[i] && 'value' in o[i]) {
              o[i] = {
                [o[i].field]: {[`$${[o[i].operator]}`] : o[i].value}
              }
            }
          traverse(o[i]);
        } 
      }
      return o
    }

    finalJson = traverse(filter, process);
    return finalJson;
  }

I did a codesandbox showing the concept: https://codesandbox.io/s/clever-tdd-jv627l?file=/src/App.tsx

I couldn´t try on refine as I wasn´t able to rebuild strapiv4 package on node_modules.

albcunha avatar Jul 14 '22 17:07 albcunha

I opened a similar issue on the hasura side for a similar feature (#2320).

I think we might want to introduce a new filter type in addition to LogicalFilter and ConditionalFilter: NestedFilter.

It could take any value:

{
  operator: "nested"
  value: {"category": {"type": {"_eq": "howto"}}},
}

smparekh avatar Aug 13 '22 19:08 smparekh

hi @omeraplak . Is this issue still open that we can look into ?

Ruchika30 avatar Oct 08 '22 15:10 Ruchika30

hi @omeraplak . Is this issue still open that we can look into ?

I did it! Happy hacking! 🚀

omeraplak avatar Oct 08 '22 16:10 omeraplak

Unassigned @Ruchika30 due to inactivity on this issue. Is there anyone wants to work on this issue? 🙏

aliemir avatar Oct 18 '22 07:10 aliemir

@aliemir

Hello, can we do something like:

{
    field: "chef.restaurants.stars",
    operator: "eq",
    value: 5,
}

And its must works only in Sptrapi provider. This solution not implements "dirty" logic in core.

p.s. Dot separated like in Laravel.

rollsover avatar Dec 06 '22 16:12 rollsover

I implemented what @rollsover proposed in the following PR #3191; tried it locally, and it's working.

npanti avatar Dec 11 '22 12:12 npanti

Hey, We've released @pankod/[email protected]. Thank you @npanti @rollsover 🎉

omeraplak avatar Dec 12 '22 16:12 omeraplak