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

Suport operators/variables in custom sorting operations. #511

Open
mattmatters opened this issue Nov 8, 2024 · 2 comments
Open

Suport operators/variables in custom sorting operations. #511

mattmatters opened this issue Nov 8, 2024 · 2 comments

Comments

@mattmatters
Copy link

mattmatters commented Nov 8, 2024

Hey folks, thanks for creating such a fantastic library. We are using it in production and it's been absolutely fantastic.

A recent issue/feature request I've come across is I need to sort a list of items by an arbitrary variable.

Example: Show tasks assigned to a specific user first before everything else.

This can obviously be handled via a fragment in the order by. I am currently considering just doing it in the query passed to flop. However it would be nice to formalize this and expose it in the same api the client currently uses.

My thought would be we could introduce another parameter alongside order_directions and order_by called order_variables (or whatever name people like). Then extend the custom field section to have something like a custom sorter. In the example above, I would pass the user id as a variable along with the sort direction.

For example, imagine the custom function was an mfa (and that the fragment was actually valid)

@derive {
  Flop.Schema,
  filterable: [:creator],
  adapter_opts: [
    custom_fields: [
      creator: [
        sorter: fn query, %Flop.Sort{variable: var, direction: dir} ->
          order_by(query, [p], [{dir, fragment("(CASE WHEN ? = ? THEN 1 ELSE 0 END) ?", p.user_id, ^var})]
        end,
        ecto_type: :integer,
        operators: [:<=, :>=]
      ]
    ]
  ]
}

Would love to hear your thoughts, more than happy to contribute a pr if it's something you would be interested in adding to your library. I'm also down if you have any suggestions for alternatives. My goal is just to have a nice consistent api for whatever frontend clients are calling this.

@woylie
Copy link
Owner

woylie commented Nov 17, 2024

Hi @mattmatters,

if you select the condition you order by in the same query, you can use an alias field: https://hexdocs.pm/flop/Flop.Schema.html#module-alias-fields. Would that work for you in this case?

We can definitely add support for custom order fields as well. I don't think we'd need an additional parameter, though. We can just extend the existing custom field options, as proposed. I'd go with an MFA tuple for consistency.

@derive {
  Flop.Schema,
  filterable: [:my_custom_field],
  sortable: [:my_custom_field],
  adapter_opts: [
    custom_fields: [
      my_custom_field: [
        filter: {MyModule, :filter_by_custom_field, []},
        sorter: {MyModule, :order_by_custom_field, []},
        ecto_type: :date,
        operators: [:<=, :>=]
      ]
    ]
  ]
}

I don't think we need a new struct for this. The sorter function can have a signature like sorter(Ecto.Query.t(), Flop.order_direction(), keyword) :: Ecto.Query.t(). The third argument would be the compile time options as defined in the schema definition above, plus runtime options passed as extra_opts (same as already done with the filter function).

For compile-time validation:

  • make both filter and sorter optional
  • if added to filterable, ensure filter option is set
  • if added to sortable, ensure sorter option is set

Would you be up for a PR?

@mattmatters
Copy link
Author

What a great idea! Custom fields make perfect sense. Yeah I would love to give a crack on it over the week!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants