bogdan / datagrid

Gem to create tables grids with sortable columns and filters
MIT License
1.02k stars 116 forks source link

AND OR conditions #212

Closed akam-it closed 7 years ago

akam-it commented 7 years ago

Is it possible to combining the AND and OR conditions?

for example I want to get records with date today AND firstname "A" OR lastname "A"

I searched issues but find nothing

bogdan commented 7 years ago

How do you imagine the UI for that?

akam-it commented 7 years ago

May be it will be two additional radiobutton "AND(default)" and "OR" and set :andonly (for example) in filter(:calldate, :datetime, :andonly ,range: true, default: proc {[1.day.ago, Time.now]})

bogdan commented 7 years ago

That looks terrible for both - end user and developer.

I would recommend you the following:

filter(:calldate, :datetime, range: true, default: ...)
filter(:name, :string) do |value|
  where("first_name = ? OR last_name = ?", value, value)
end

It hardly depends on which functionality you want to be available for the end user. What I learned from years of making grids: users never want OR. If you need OR between filters, you do something wrong.

akam-it commented 7 years ago

There is call history table. In one scenario I want to find inbound OR outbound calls to/from number 100 only today. In the second scenario I want to find only inbound calls to 100 AND from 101.

There is no other way to do it without OR :(

bogdan commented 7 years ago

Here is the way:

filter(:inbound_number, :integer, dummy: true)
filter(:outbound_number, :integer, dummy: true)
filter(:conjuction, :enum, select: %w(AND OR), include_blank: false, default: 'AND') do |conjuction, scope, grid|
  scope.where(
    "outbound_number = ? #{conjuction} inbound_number = ?", 
    grid.inbound_number, grid.outbound_number
  )
end
akam-it commented 7 years ago

Thank you for your try, but not working

  1. dummy filter can't be dynamic filter? (with operations: %w(= =~))
  2. If fields are empty that sql generate - AND (outbound_number = NULL AND inbound_number = NULL)
bogdan commented 7 years ago
  1. Yes, they can.
  2. Sorry I didn't handle nil cases.

I also want to note that things you are trying to do are really advanced, therefore there will be no 3 lines of code solution.

But here it is:

filter(:inbound_number, :dynamic,
  include_blank: false, operation: %w(= =~), select: [:inbound_number], dummy: true)

filter(:outbound_number, :dynamic, 
  include_blank: false, operation: %w(= =~), select: [:outbound_number], dummy: true)

OPS_SQL_MAP = {"=" => "=", "=~" => "like"}
filter(
  :conjuction, :enum, select: %w(AND OR), include_blank: false, default: 'AND'
) do |conjuction, scope, grid|
  conditions = [grid.inbound_number , grid.outbound_number].select(&:last)
  query = conditions.map  do |attribute, operation, value|
   value = "%#{value}%" if operation == "=~"
    " #{attribute} #{OPS_SQL_MAP[operation]} #{ActiveRecord::Base.sanitize(value)} "
  end.join(conjuction)
  scope.where(query)
end
akam-it commented 7 years ago

Thank you for help!