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 condition #10

Closed
RafaelMCarvalho opened this issue Nov 13, 2015 · 4 comments
Closed

Add OR condition #10

RafaelMCarvalho opened this issue Nov 13, 2015 · 4 comments

Comments

@RafaelMCarvalho
Copy link

Would be nice to have something like:

apply_filters(resource, { or: { title: "Ruby", content: "Rails" })

What do you think?

@vasilakisfil
Copy link
Contributor

Yes it's on my list for version 2.0 which will also be JSON API compliant. I am not sure how the API for #or will look like (maybe like you suggest) but it will exploit rails 5 AR #OR method. Probably it will land around December unless you want to send a pull request :)

@vasilakisfil vasilakisfil mentioned this issue Apr 30, 2016
12 tasks
@KRaymundus
Copy link

Great Lib! Thnx!

I've been trying to implement or-filters, here is my result so far:
In the column filters I added the or_filters function (down below), and at each null_filters I added a call to or_filters:

module ActiveHashRelation::ColumnFilters
  def filter_primary(resource, column, param)
    resource = resource.where(id: param)
  end

  def filter_integer(resource, column, table_name, param)
    if param.is_a? Array
      n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
      return resource.where("#{table_name}.#{column} IN (#{n_param})")
    elsif param.is_a? Hash
      if !param[:null].nil?
        return null_filters(resource, table_name, column, param)
      elsif param[:or]
        return or_filters(resource, table_name, column, param, __method__)
      else
        return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
      end
    else
      return resource.where("#{table_name}.#{column} = ?", param)
    end
  end

  def filter_float(resource, column, table_name, param)
    filter_integer(resource, column, table_name, param)
  end

  def filter_decimal(resource, column, table_name, param)
    filter_integer(resource, column, table_name, param)
  end

  def filter_string(resource, column, table_name, param)
    if param.is_a? Array
      n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
      return resource.where("#{table_name}.#{column} IN (#{n_param})")
    elsif param.is_a? Hash
      if !param[:null].nil?
        return null_filters(resource, table_name, column, param)
      elsif param[:or]
        return or_filters(resource, table_name, column, param, __method__)
      else
        return apply_like_filters(resource, table_name, column, param)
      end
    else
      return resource.where("#{table_name}.#{column} = ?", param)
    end
  end

  def filter_text(resource, column, param)
    return filter_string(resource, column, param)
  end

  def filter_date(resource, column, table_name, param)
    if param.is_a? Array
      n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
      return resource.where("#{table_name}.#{column} IN (#{n_param})")
    elsif param.is_a? Hash
      if !param[:null].nil?
        return null_filters(resource, table_name, column, param)
      elsif param[:or]
        return or_filters(resource, table_name, column, param, __method__)
      else
        return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
      end
    else
      resource = resource.where(column => param)
    end

    return resource
  end

  def filter_datetime(resource, column, table_name, param)
    if param.is_a? Array
      n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
      return resource = resource.where("#{table_name}.#{column} IN (#{n_param})")
    elsif param.is_a? Hash
      if !param[:null].nil?
        return null_filters(resource, table_name, column, param)
      elsif param[:or]
        return or_filters(resource, table_name, column, param, __method__)
      else
        return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
      end
    else
      resource = resource.where(column => param)
    end

    return resource
  end

  def filter_boolean(resource, column, param)
    b_param = ActiveRecord::Type::Boolean.new.type_cast_from_database(param)

    resource = resource.where(column => b_param)
  end

  private

  def apply_leq_geq_le_ge_filters(resource, table_name, column, param)
    if !param[:leq].blank?
      resource = resource.where("#{table_name}.#{column} <= ?", param[:leq])
    elsif !param[:le].blank?
      resource = resource.where("#{table_name}.#{column} < ?", param[:le])
    end

    if !param[:geq].blank?
      resource = resource.where("#{table_name}.#{column} >= ?", param[:geq])
    elsif !param[:ge].blank?
      resource = resource.where("#{table_name}.#{column} > ?", param[:ge])
    end

    return resource
  end

  def apply_like_filters(resource, table_name, column, param)
    like_method = "LIKE"
    like_method = "ILIKE" if param[:with_ilike]

    if !param[:starts_with].blank?
      resource = resource.where("#{table_name}.#{column} #{like_method} ?", "#{param[:starts_with]}%")
    end

    if !param[:ends_with].blank?
      resource = resource.where("#{table_name}.#{column} #{like_method} ?", "%#{param[:ends_with]}")
    end

    if !param[:like].blank?
      resource = resource.where("#{table_name}.#{column} #{like_method} ?", "%#{param[:like]}%")
    end

    if !param[:eq].blank?
      resource = resource.where("#{table_name}.#{column} = ?", param[:eq])
    end

    return resource
  end
  
  def null_filters(resource, table_name, column, param)
    if param[:null] == true
      resource = resource.where("#{table_name}.#{column} IS NULL")
    end
    
    if param[:null] == false
      resource = resource.where("#{table_name}.#{column} IS NOT NULL")
    end
    
    return resource
  end

  def or_filters(resource, table_name, column, param, filter_method)
    base_resource = resource  # Save base, so to make each or_filter work on the same base-set
    param[:or] = param[:or].map { |k, v| {k => v} } # Convert to array
    param[:or].each_with_index { |p, i|
      or_filter = send(filter_method, base_resource, column, table_name, p)
      if i == 0
        resource = or_filter  # Nothing to 'or' on for the first or filter
      else
        resource = resource.or(or_filter)
      end
    }
    return resource
  end
end

It is to be called as suggested: apply_filters(resource, { or: { title: "Ruby", content: "Rails" })
It also works with null filters.

One thing I don't understand is why I have to overwrite the param[:or] = in my function. If I assign the (to array converted param) to a local variable it doesn't work. Any ideas?

Didn't test well yet, but it might be a good start.

@vasilakisfil
Copy link
Contributor

Thanks for the feedback @KRaymundus. As I said, I am adding tests at the moment in order to move faster without breaking anything and include features such as this one. I will probably finish adding tests end of this week and I will ping you then so we can take a look on your feedback and merge to master.

@vasilakisfil
Copy link
Contributor

@RafaelMCarvalho @KRaymundus thank you for your input. The OR filter has implemented on master. Check the documentation ;). Closing here.

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

No branches or pull requests

3 participants