Skip to content

About filterQuery #1161

Closed
Closed

Description

@vonagam

In filterQuery we have operators and filters. They are kinda on equal ground. operators work on query part of result, and filters define filters part.

But in AdapterService we see inequality in treatment of those two. Here we see how operators value comes from options.whitelist (and they have unneeded default value), while filters come from options.filters. And while they are both actually lists of additional keys, naming moves focus to whitelist/operators/query part for adapter developers.

Result of this focus you can see, for example, in feathers-mongoose. $populate is a filter type (as it was before changes), but now it is told to go in whitelist and, so, to query part. It results in those manual removals of $populate from query (1, 2, 3, 4), which would not be needed if instead of whitelist, $populate was expected in filters.

Ideally, i think options should have been named operatorsWhitelist/filtersWhitelist, additionalOperators/additionalFilters, whitelistedOperators/whitelistedFilters or something like this. Plus it should be clearly stated somewhere for adapter developers what is the difference between a filter and an operator: the most simple rule is if something has to be at root level and can't be nested in $or or $and (like $populate for example) then it is a filter, otherwise operator.

For starters, we can add aliases to whitelist/filters pair and in next major release remove whitelist/filters.

I know that this is not too important, but still it creates some inconvenience when you can't rely on service.filterQuery to really separate query from filters.

What are your thoughts?

Activity

  1. daffl commented on Jan 10, 2019

    @daffl
    Member

    I think it's a good distinction for operators to be at the top level and $populate should indeed be an operator. It definitely should still be enabled explicitly though since it has certain implications (like protected fields that have to be removed explicitly). Could you also add a code example for what doesn't work as intended. I think I understand the explanation but I think it would be easier to follow (e.g. service.filterQuery result for a query expected vs. actual).

  2. vonagam commented on Jan 10, 2019

    @vonagam
    MemberAuthor

    Yes, i agree about enabling it explicitly. Only not through whitelist, but through filters.

    Filters must be at the top level in query (like $sort, $limit, $skip, $select do).
    Operators (like $ne, $gt, $nin) can be at the top, but also can be nested in aggregate operators (like $or or $and).

    Before changes, $populate was in filters. Now it is expected in query part. Why the change? How do you distinct between custom operator and custom filter?

    So the answer to "what doesn't work as intended?" is treatment of all additional $ keys as operators (so they all expected to go into whitelist and query), while some of them are actually filters.

    In mongoose service case, $populate ends up in query part and inside of service you always _.omit(query, '$populate'). Isn't it a signal that $populate should be a filter instead?

  3. matiaslopezd commented on Apr 25, 2022

    @matiaslopezd

    @daffl this is still applying to all providers? I mean server and external? I think have sense if only apply to external request.

    Also, could be awesome to allow adding custom rules like filter operators based on local IP for microservices communication.

    service({
      operators: (context) => {
        const { ip, headers } = context.params;
        const { authorization, microservice } = headers;
        if (IPWhitelist.includes(ip) && authorization === 'mysecret' && microservice === 'another validation') return ['$regex', '$options'];
        return ['$eq'];
      }
    });

    To do this if the operators or filters key is a function could pass the context as an argument expecting an array.

    Is possible? What do you think?

  4. daffl commented on Apr 27, 2022

    @daffl
    Member

    This should now be possible by setting params.adapter in a hook which lets you overwrite the setting dynamically:

    context => {
      const { params } = context
      const { headers = {} } = context
    
      if (ipIsAllowed(headers)) {
        context.params = {
          ...params,
          adapter: {
            operators: ['$regex']
          }
        }
      }
    
      return context
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions