2014-11-23 12 views
6

Ho un progetto su Ruby on Rails 4.1.4, utilizzando activeadmin 1.0.0.pre from git://github.com/activeadmin/activeadmin, pg 0.17.1, PostgreSQL 9.3Rails + ActiveAdmin - filtraggio con ransacker genera un errore PG :: SyntaxError: ERRORE: errore di sintassi o in prossimità ""

Nel progetto ho questi modelli:

  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

Ho un compito di implementare un filtro ActiveAdmin sulla pagina di indice, chiamato "by_active_projects", quindi deve mostrare gli utenti che hanno definito il numero di attivi progetti, ovvero progetti che hanno archive == false. E.g. se digito '2' nel filtro, deve trovare tali account che hanno esattamente 2 progetti attivi.

Per ora ho registrato risorsa "Account" in ActiveAdmin, ed entro admin/account.rb ho aggiunto filter :by_active_projects_eq

Dopo che ho definito un ambito having_active_projects per il modello account (modelli/account.rb):

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

fase successiva, ho definito un ransacker per il modello di account in questo modo:

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

Nello sviluppo DB c'è un account, che ha esattamente 8 progetti attivi, e il filtraggio funziona alla grande. Ma quando ho provato a filtrare gli account per 2 progetti attivi, ho riscontrato un errore. Nel DB ci sono tre tali conti, e la pagina di errore mi ha riferito che la sintassi di query è sbagliata:

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 

Come si può vedere, invece di

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

questa cosa viene generato:

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

ho cercato di scavare nel codice sorgente, muovendosi con i punti sui pezzi di codice all'interno del lib ActiveRecord, ActiveAdmin e Ransack e capito che la relazione è in costruzione con l'aiuto di Arel :: Nodes :: Equality. Non sono sicuro che questo sia il motivo, ma posso dirlo con certezza:

lib/active_record/relation/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 qui è un rapporto Active Record per l'account;

prima di chiamare build_where sulla riga # 565, self.to_sql uguale a

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

dopo aver chiamato e assegnare il risultato a self.where_values,

self.to_sql uguale a

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 

Qualsiasi aiuto o le informazioni sull'argomento sono apprezzate! Grazie!

risposta

4

così ho trovato la soluzione:

In primo luogo, ho cambiato il mio filtro in admin/account.rb da

filter :by_active_projects_eq 

a

filter :by_active_projects_in, 
     :as => :string 

Questo approccio ha portato nella generazione SQL corretta,

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

Dopo di che ho anche dovuto cambiare il mio ransacker da

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

a

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 

perché il modo in cui è stato implementato anche causato query non corretta: ad esempio, non ci sono tali account che hanno esattamente 5 progetti attivi . In tal caso

data = Account.having_active_projects(v).pluck(:id) 

restituito "array vuoto", e si occupano di questo array con data ||= nil mai effettivamente tornato nil, e che ha portato in SQL come questo:

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 

avviso la parte "accounts"."id" IN(), che era causando problemi.

Dopo aver sostituito data ||= nil con data.present? ? data : nil, se data non era presente è stato assegnato un nil, e quella parte in SQL è stato generato corectly: "accounts"."id" IN (NULL)