Rails + ActiveAdmin - 使用ransacker进行过滤会抛出错误PG :: SyntaxError:ERROR:语法错误在“,”附近

时间:2014-11-23 21:32:36

标签: ruby postgresql ruby-on-rails-4 activeadmin ransack

我在Ruby on Rails 4.1.4上有一个项目,使用activeadmin 1.0.0.pre from git://github.com/activeadmin/activeadminpg 0.17.1,PostgreSQL 9.3

在项目中我有这些模型:

  1. class User has_one :account

  2. class Account belongs_to :user has_many :project_accounts has_many :projects, :through => :project_accounts

  3. class Project # the project has a boolean attribute 'archive' has_many :project_accounts

  4. class ProjectAccount belongs_to :account belongs_to :project

  5. 我有一项任务是在索引页面上实现一个ActiveAdmin过滤器,名为" by_active_projects",因此它必须显示已定义活动项目数的用户,这意味着此类项目有archive == false的人。 例如。如果我输入' 2'在过滤器中,它必须找到具有正好2个活动项目的帐户。

    现在我已注册"帐号" ActiveAdmin中的资源,admin/account.rb内已添加filter :by_active_projects_eq

    之后,我为帐户模型(models / account.rb)定义了范围having_active_projects

    scope :having_active_projects, ->(number) { joins(:projects).where("projects.archive = ?", false).having("count(projects) = ?", number).group("accounts.id") }
    

    下一步,我已经为此帐户模型定义了一个搜索者:

    ransacker :by_active_projects, formatter: proc{ |v|
        data = Account.having_active_projects(v).map(&:id)
        data ||= nil
      } do |parent|
        parent.table[:id]
      end
    

    在开发数据库中有一个帐户,它有8个活动项目,过滤效果很好。但是,当我试图通过2个活动项目过滤帐户时,我遇到了错误。在DB中有三个这样的帐户,错误页面报告我查询语法错误:

    SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114' LIMIT 30 OFFSET 0) subquery_for_count
    

    如您所见,而不是

    "accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114')
    

    正在生成这个东西:

    "accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114'
    

    我试图深入研究源代码,在ActiveRecord,ActiveAdmin和Ransack lib中的代码段上移动断点,并发现在 <的帮助下构建关系EM>阿雷尔::节点::平等 即可。我不确定这是原因,但我可以肯定地说:

    LIB / active_record /关系/ query_methods.rb `

    560   def where!(opts = :chain, *rest)
    561      if opts == :chain
    562        WhereChain.new(self)
    563      else
    564        references!(PredicateBuilder.references(opts)) if Hash === opts
    565        self.where_values += build_where(opts, rest)
    566        self
    567      end
    568    end`
    

    self这是帐户的活动记录关系;

    在第565行调用build_where之前,self.to_sql等于

    SELECT "accounts".* FROM "accounts"  WHERE "accounts"."deleted_at" IS NULL  ORDER BY "accounts"."created_at" desc
    

    调用它并将结果分配给self.where_values

    self.to_sql等于

    SELECT "accounts".* FROM "accounts"  WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114'  ORDER BY "accounts"."created_at" desc
    

    有关此事的任何帮助或信息表示赞赏!谢谢!

1 个答案:

答案 0 :(得分:4)

所以我找到了解决方案:

首先,我在<{1}}中更改了我的过滤器

admin/account.rb

filter :by_active_projects_eq

这种方法导致了正确的SQL生成,

filter :by_active_projects_in,
         :as => :string

之后我还必须从

改变我的搜索者
"accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114')

ransacker :by_active_projects, formatter: proc{ |v|
    data = Account.having_active_projects(v).map(&:id)
    data ||= nil
  } do |parent|
    parent.table[:id]
  end

因为它的实现方式也导致了不正确的查询:例如,没有这样的帐户只有5个活动项目。在那种情况下

ransacker :by_active_projects, formatter: proc{ |v|
    data = Account.having_active_projects(v).pluck(:id)
    data.present? ? data : nil
  } do |parent|
    parent.table[:id]
  end

返回“Empty Array”,处理这个带有data = Account.having_active_projects(v).pluck(:id) 的数组从未实际返回data ||= nil,导致SQL如下:

nil

注意导致问题的SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" IN () LIMIT 30 OFFSET 0) subquery_for_count 部分。

"accounts"."id" IN ()替换为data ||= nil后,如果data.present? ? data : nil不存在,则会为其分配data,并且SQL中的该部分是核心生成的:nil