Reputation: 133
I am trying to pass a couple IDs through to a custom ransacker filter for active admin
filter :hasnt_purchased_product_items_in, as: :select, collection: -> { ProductItem.all }, multiple: true
The ransacker code
ransacker :hasnt_purchased_product_items, formatter: proc { |product_id|
users_who_purchased_ids = User.joins(:orders => :items).where("orders.status = 'success'").where("order_items.orderable_id IN (?)", product_id) # return only id-s of returned items.
ids = User.where.not(id: users_who_purchased_ids).pluck(:id)
ids.present? ? ids : nil # return ids OR nil!
} do |parent| # not sure why this is needed .. but it is :)
parent.table[:id]
end
This works for a single query but not for multiple, the SQL is doing 2 separate searches instead of one
It's running the following 2 statements
order.items.orderable_id IN ('90')
and
order.items.orderable_id IN ('91')
in 2 separate SQL statements, instead of what i want which is
order.items.orderable_id IN ('90','91')
the submitted info from the active admin page is
q[hasnt_purchased_product_items_in][]: 90
q[hasnt_purchased_product_items_in][]: 91
i think i need to do something to the incoming parameter at the ransacker stage but i'm not sure how to deal with that
Upvotes: 1
Views: 1474
Reputation: 4877
It shouldn't be stepping in. The param should be |product_ids| and give us an array object inside the proc that then gets used. Your ransacker should return an active record association or an arel query.
I would try each of these:
ransacker :hasnt_purchased_product_items_in do |product_ids|
users_who_purchased_ids = User.joins(orders: :items).where(orders: {status: 'success'}, order_items: {orderable_id: product_ids}) # return only id-s of returned items.
User.where.not(id: users_who_purchased_ids)
end
ransacker :hasnt_purchased_product_items do |product_ids|
users_who_purchased_ids = User.joins(orders: :items).where(orders: {status: 'success'}, order_items: {orderable_id: product_ids}) # return only id-s of returned items.
User.where.not(id: users_who_purchased_ids)
end
I've had to guess the object to return because it's not clear which model this ransacker is on. If you can tell me the objective (e.g. filtering product recommendations for a user to exclude their purchases) then I can update the answer.
User.rb
scope :hasnt_purchased_product_items_in, ->(product_ids){
users_who_purchased_ids = joins(orders: :items).where(orders: {status: 'success'}, order_items: {orderable_id: product_ids}) # return only id-s of returned items.
where.not(id: users_who_purchased_ids)
}
def self.ransackable_scopes(auth_object = nil)
%i(hasnt_purchased_product_items_in)
end
Then this works:
User.ransack({hasnt_purchased_product_items_in: [[1,2,3]]}).result
Note that the ransack want's an array of args. We want one arg, which is an array.
Upvotes: 2