Filtering for Json Keys as Columns in Active Admin
After implementing sorting on keys of a json column data, I need to add filtering too. We are using
ActiveAdmin(1.0.0-pre1) with postgres 9.4. You can check my
previous post regarding sorting.
For sake of continuity, lets take the same example as in the previous post. We have a model named Box which
stores (length, width & height) as json in a column named dimensions.
In ActiveAdmin, the filters can be displayed as below
12345678910111213141516171819
ActiveAdmin.registerBoxdofilter:length,as::numeric,label:'Length'filter:breadth,as::numeric,label:'Breadth'filter:height,as::numeric,label:'Height'filter:volume,as::numeric,label:'Volume'controllerdodefscoped_collectionBox.select("*, dimensions ->> 'length' as length, dimensions ->> 'breadth' as breadth, dimensions ->> 'height' as height")endendindexdocolumn:length,label:'Length',sortable:"dimensions->>'length'"column:breadth,label:'Breadth',sortable:"dimensions->>'breadth'"column:height,label:'Height',sortable:"dimensions->>'height'"column:volume,label:'Volume',sortable:trueendend
Since some of the fields are not associated with the table, we need need to define custom ransackers for them.
ActiveAdmin uses ransack gem for implementing searching.
1234567891011121314151617181920212223242526
classBox<ActiveRecord::Baseransacker:lengthdo|parent|# '->>' is a part of the syntax for accessing the json keys, 'dimensions' the column name and # 'length' is the actual key on which the search is to be implemented.Arel::Nodes::InfixOperation.new('->>',parent.table[:dimensions],Arel::Nodes.build_quoted('length'))endransacker:breadthdo|parent|Arel::Nodes::InfixOperation.new('->>',parent.table[:dimensions],Arel::Nodes.build_quoted('breadth'))endransacker:heightdo|parent|Arel::Nodes::InfixOperation.new('->>',parent.table[:dimensions],Arel::Nodes.build_quoted('height'))end# The below ransackers do not work. Also, we need to declare three ransackers :length_equals, :length_greater_than & # :length_less_than for each field. Dont know the reason but am looking into it.ransacker:length_greater_than,formatter:->(value){data=where("dimensions ->> 'length' > '#{value}'").pluck:iddata=data.present??data:nil}do|parent|parent.table[:id]endend
This still won’t work. The filters are displayed weirdly(dropdown on top and search input field at the bottom) rather in
the expected layout(dropdown on left and search input field on the right). This is because ActiveAdmin adds a class
select_and_search which decides if a filter field is searchable or not. This class is responsible for the proper
layout and working of filters. The below code is referenced from ActiveAdmin. It decides whether the class is to be
added or not when the filter is initialized.
1234567891011121314151617181920212223242526
moduleActiveAdminmoduleInputsmoduleFiltersmoduleBasemoduleSearchMethodSelectdefwrapper_html_optionsopts=super(opts[:class]||='')<<' select_and_search'unlessseems_searchable?optsendendendendendendmoduleActiveAdminmoduleFiltersmoduleFormtasticAddonsdefseems_searchable?# ransacker? checks if any custom ransackers are defined. has_predicate?||ransacker?||scope?endendendend
So, we remove the check for ransacker? by adding a patch in the initializers.
Now, the filters will be displayed properly and would send the selected option(Greater than, Equals or Less than) with
the specified value to the ransackers.
I am not inclined towards overriding the library methods but haven’t found any other solution for the same. So, for now,
this it the hack that works for me.
Working source code can be found here. Hope it was interesting.