如何在没有硬编码的情况下构建查询

时间:2021-03-23 23:06:05

标签: ruby-on-rails

我正在构建一个 Rails 5.2 应用程序。 在这个应用程序中,我有一个 QueryBuilder 类,我的用户可以使用它来构建数据查询。 该类使用 JSON 对象来构建和运行查询。

我正在使用这里的代码:

https://stackoverflow.com/a/47120339/339067

数据

data = [{
            className: 'Project',
            relation: 'Project',
            field: 'title',
            operator: 'starts_with',
            chainer: 'AND',
            val: 'Rebranding'
        }, {
            className: 'Project',
            relation: 'Project',
            field: 'id',
            operator: 'not_equals',
            chainer: 'AND NOT',
            val: '10'
        }]

查询构建器

class QueryBuilder
  def initialize(data)
    @data = prepare_data(data)
  end
  
  def find_records
    queries = @data.group_by {|e| e[:model]}
    queries.map do |k, v|
      puts k
      puts v
      q = v.map do |f|
        {
          field: "#{f[:table_name]}.#{f[:field]} #{read_operator(f[:operator])} ?",
          value: value_based_on_operator(f[:val], f[:operator])
        }
      end
      db_query = q.map {|e| e[:field]}.join(" AND ")
      values = q.map {|e| e[:value]}
      {"#{k}": k.constantize.joins(join_hash(v)).where(db_query, *values)}
    end
  end

  private

  def join_hash(array_of_relations)
    hash = {}
    array_of_relations.each do |f|
      hash.merge!(array_to_hash(f[:joins]))
    end
    hash.map do |k, v|
      if v.nil?
        k
      else
        {"#{k}": v}
      end
    end
  end

  def read_operator(operator)
    case operator
    when 'equals'
      '='
    when 'not_equals'
      '!='
    when 'larger_than'
      '>'
    when 'smaller_than'
      '<'
    when 'starts_with'
      'LIKE'
    end
  end

  def value_based_on_operator(value, operator)
    case operator
    when 'equals'
      value
    when 'not_equals'
      value
    when 'larger_than'
      value
    when 'smaller_than'
      value
    when 'starts_with'
      "%#{value}"
    end
  end

  def prepare_data(data)
    data.each do |record|
      record.tap do |f|
        f[:model] = f[:relation].split('.')[0]
        f[:joins] = f[:relation].split('.').drop(1)
        f[:table_name] = f[:className].constantize.table_name
      end
    end
  end

  def array_to_hash(array)
    if array.length < 1
      {}
    elsif array.length == 1
      {"#{array[0]}": nil}
    elsif array.length == 2
      {"#{array[0]}": array[1]}
    else
      {"#{array[0]}": array_to_hash(array.drop(1))}
    end
  end
end

我是这样用的

query = QueryBuilder.new(data)
render plain: query.find_records.to_json, status: 200

如何使用“chainer”对象值而不是对上述类中的 AND 进行硬编码?

0 个答案:

没有答案