Skip to content

Commit 9abc7f9

Browse files
committed
Add custom search feature. Closes #343, Closes #3019
1 parent c6796b0 commit 9abc7f9

File tree

8 files changed

+68
-30
lines changed

8 files changed

+68
-30
lines changed

lib/rails_admin/adapters/active_record.rb

+10-6
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,17 @@ def build
118118
end
119119

120120
def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))
121-
wb = WhereBuilder.new(scope)
122-
fields.each do |field|
123-
value = parse_field_value(field, query)
124-
wb.add(field, value, field.search_operator)
121+
if config.list.search_by
122+
scope.send(config.list.search_by, query)
123+
else
124+
wb = WhereBuilder.new(scope)
125+
fields.each do |field|
126+
value = parse_field_value(field, query)
127+
wb.add(field, value, field.search_operator)
128+
end
129+
# OR all query statements
130+
wb.build
125131
end
126-
# OR all query statements
127-
wb.build
128132
end
129133

130134
# filters example => {"string_field"=>{"0055"=>{"o"=>"like", "v"=>"test_value"}}, ...}

lib/rails_admin/adapters/mongoid.rb

+16-12
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ def all(options = {}, scope = nil)
4242
scope = scope.includes(*options[:include]) if options[:include]
4343
scope = scope.limit(options[:limit]) if options[:limit]
4444
scope = scope.any_in(_id: options[:bulk_ids]) if options[:bulk_ids]
45-
scope = scope.where(query_conditions(options[:query])) if options[:query]
46-
scope = scope.where(filter_conditions(options[:filters])) if options[:filters]
45+
scope = query_scope(scope, options[:query]) if options[:query]
46+
scope = filter_scope(scope, options[:filters]) if options[:filters]
4747
if options[:page] && options[:per]
4848
scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per])
4949
end
@@ -113,21 +113,25 @@ def make_field_conditions(field, value, operator)
113113
conditions_per_collection
114114
end
115115

116-
def query_conditions(query, fields = config.list.fields.select(&:queryable?))
117-
statements = []
116+
def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))
117+
if config.list.search_by
118+
scope.send(config.list.search_by, query)
119+
else
120+
statements = []
118121

119-
fields.each do |field|
120-
value = parse_field_value(field, query)
121-
conditions_per_collection = make_field_conditions(field, value, field.search_operator)
122-
statements.concat make_condition_for_current_collection(field, conditions_per_collection)
123-
end
122+
fields.each do |field|
123+
value = parse_field_value(field, query)
124+
conditions_per_collection = make_field_conditions(field, value, field.search_operator)
125+
statements.concat make_condition_for_current_collection(field, conditions_per_collection)
126+
end
124127

125-
statements.any? ? {'$or' => statements} : {}
128+
scope.where(statements.any? ? {'$or' => statements} : {})
129+
end
126130
end
127131

128132
# filters example => {"string_field"=>{"0055"=>{"o"=>"like", "v"=>"test_value"}}, ...}
129133
# "0055" is the filter index, no use here. o is the operator, v the value
130-
def filter_conditions(filters, fields = config.list.fields.select(&:filterable?))
134+
def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))
131135
statements = []
132136

133137
filters.each_pair do |field_name, filters_dump|
@@ -145,7 +149,7 @@ def filter_conditions(filters, fields = config.list.fields.select(&:filterable?)
145149
end
146150
end
147151

148-
statements.any? ? {'$and' => statements} : {}
152+
scope.where(statements.any? ? {'$and' => statements} : {})
149153
end
150154

151155
def parse_collection_name(column)

lib/rails_admin/config/sections/list.rb

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class List < RailsAdmin::Config::Sections::Base
2424
false
2525
end
2626

27+
register_instance_option :search_by do
28+
nil
29+
end
30+
2731
register_instance_option :sort_by do
2832
parent.abstract_model.primary_key
2933
end

spec/dummy_app/app/active_record/player.rb

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class Player < ActiveRecord::Base
1414

1515
before_destroy :destroy_hook
1616

17+
scope :rails_admin_search, ->(query) { where(name: query.reverse) }
18+
1719
def destroy_hook; end
1820

1921
def draft_id

spec/dummy_app/app/mongoid/player.rb

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class Player
2828

2929
before_destroy :destroy_hook
3030

31+
scope :rails_admin_search, ->(query) { where(name: query.reverse) }
32+
3133
def destroy_hook; end
3234

3335
def draft_id

spec/integration/basic/list/rails_admin_basic_list_spec.rb

+22
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,28 @@ def visit_page(page)
505505
end
506506
end
507507

508+
describe 'Custom search' do
509+
before do
510+
RailsAdmin.config do |config|
511+
config.model Player do
512+
list do
513+
search_by :rails_admin_search
514+
end
515+
end
516+
end
517+
end
518+
let!(:players) do
519+
[FactoryBot.create(:player, name: 'Joe'),
520+
FactoryBot.create(:player, name: 'George')]
521+
end
522+
523+
it 'performs search using given scope' do
524+
visit index_path(model_name: 'player', query: 'eoJ')
525+
is_expected.to have_content(players[0].name)
526+
is_expected.to have_no_content(players[1].name)
527+
end
528+
end
529+
508530
describe 'list for objects with overridden to_param' do
509531
before do
510532
@ball = FactoryBot.create :ball

spec/rails_admin/adapters/active_record_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class PlayerWithDefaultScope < Player
128128
end
129129
end
130130

131-
describe '#query_conditions' do
131+
describe '#query_scope' do
132132
let(:abstract_model) { RailsAdmin::AbstractModel.new('Team') }
133133

134134
before do
@@ -155,7 +155,7 @@ class PlayerWithDefaultScope < Player
155155
end
156156
end
157157

158-
describe '#filter_conditions' do
158+
describe '#filter_scope' do
159159
let(:abstract_model) { RailsAdmin::AbstractModel.new('Team') }
160160

161161
before do

spec/rails_admin/adapters/mongoid_spec.rb

+10-10
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@
188188
end
189189
end
190190

191-
describe '#query_conditions' do
191+
describe '#query_scope' do
192192
before do
193193
@abstract_model = RailsAdmin::AbstractModel.new('Player')
194194
@players = [{}, {name: 'Many foos'}, {position: 'foo shortage'}].
@@ -200,7 +200,7 @@
200200
end
201201
end
202202

203-
describe '#filter_conditions' do
203+
describe '#filter_scope' do
204204
before do
205205
@abstract_model = RailsAdmin::AbstractModel.new('Player')
206206
@team = FactoryBot.create :team, name: 'king of bar'
@@ -341,17 +341,17 @@
341341
end
342342

343343
it 'supports date type query' do
344-
expect(@abstract_model.send(:filter_conditions, 'date_field' => {'1' => {v: ['', 'January 02, 2012', 'January 03, 2012'], o: 'between'}})).to eq('$and' => [{'date_field' => {'$gte' => Date.new(2012, 1, 2), '$lte' => Date.new(2012, 1, 3)}}])
345-
expect(@abstract_model.send(:filter_conditions, 'date_field' => {'1' => {v: ['', 'January 03, 2012', ''], o: 'between'}})).to eq('$and' => [{'date_field' => {'$gte' => Date.new(2012, 1, 3)}}])
346-
expect(@abstract_model.send(:filter_conditions, 'date_field' => {'1' => {v: ['', '', 'January 02, 2012'], o: 'between'}})).to eq('$and' => [{'date_field' => {'$lte' => Date.new(2012, 1, 2)}}])
347-
expect(@abstract_model.send(:filter_conditions, 'date_field' => {'1' => {v: ['January 02, 2012'], o: 'default'}})).to eq('$and' => [{'date_field' => {'$gte' => Date.new(2012, 1, 2), '$lte' => Date.new(2012, 1, 2)}}])
344+
expect(@abstract_model.send(:filter_scope, FieldTest, 'date_field' => {'1' => {v: ['', 'January 02, 2012', 'January 03, 2012'], o: 'between'}}).selector).to eq('$and' => [{'date_field' => {'$gte' => Date.new(2012, 1, 2), '$lte' => Date.new(2012, 1, 3)}}])
345+
expect(@abstract_model.send(:filter_scope, FieldTest, 'date_field' => {'1' => {v: ['', 'January 03, 2012', ''], o: 'between'}}).selector).to eq('$and' => [{'date_field' => {'$gte' => Date.new(2012, 1, 3)}}])
346+
expect(@abstract_model.send(:filter_scope, FieldTest, 'date_field' => {'1' => {v: ['', '', 'January 02, 2012'], o: 'between'}}).selector).to eq('$and' => [{'date_field' => {'$lte' => Date.new(2012, 1, 2)}}])
347+
expect(@abstract_model.send(:filter_scope, FieldTest, 'date_field' => {'1' => {v: ['January 02, 2012'], o: 'default'}}).selector).to eq('$and' => [{'date_field' => {'$gte' => Date.new(2012, 1, 2), '$lte' => Date.new(2012, 1, 2)}}])
348348
end
349349

350350
it 'supports datetime type query' do
351-
expect(@abstract_model.send(:filter_conditions, 'datetime_field' => {'1' => {v: ['', 'January 02, 2012 00:00', 'January 03, 2012 00:00'], o: 'between'}})).to eq('$and' => [{'datetime_field' => {'$gte' => Time.local(2012, 1, 2), '$lte' => Time.local(2012, 1, 3).end_of_day}}])
352-
expect(@abstract_model.send(:filter_conditions, 'datetime_field' => {'1' => {v: ['', 'January 03, 2012 00:00', ''], o: 'between'}})).to eq('$and' => [{'datetime_field' => {'$gte' => Time.local(2012, 1, 3)}}])
353-
expect(@abstract_model.send(:filter_conditions, 'datetime_field' => {'1' => {v: ['', '', 'January 02, 2012 00:00'], o: 'between'}})).to eq('$and' => [{'datetime_field' => {'$lte' => Time.local(2012, 1, 2).end_of_day}}])
354-
expect(@abstract_model.send(:filter_conditions, 'datetime_field' => {'1' => {v: ['January 02, 2012 00:00'], o: 'default'}})).to eq('$and' => [{'datetime_field' => {'$gte' => Time.local(2012, 1, 2), '$lte' => Time.local(2012, 1, 2).end_of_day}}])
351+
expect(@abstract_model.send(:filter_scope, FieldTest, 'datetime_field' => {'1' => {v: ['', 'January 02, 2012 00:00', 'January 03, 2012 00:00'], o: 'between'}}).selector).to eq('$and' => [{'datetime_field' => {'$gte' => Time.local(2012, 1, 2), '$lte' => Time.local(2012, 1, 3).end_of_day}}])
352+
expect(@abstract_model.send(:filter_scope, FieldTest, 'datetime_field' => {'1' => {v: ['', 'January 03, 2012 00:00', ''], o: 'between'}}).selector).to eq('$and' => [{'datetime_field' => {'$gte' => Time.local(2012, 1, 3)}}])
353+
expect(@abstract_model.send(:filter_scope, FieldTest, 'datetime_field' => {'1' => {v: ['', '', 'January 02, 2012 00:00'], o: 'between'}}).selector).to eq('$and' => [{'datetime_field' => {'$lte' => Time.local(2012, 1, 2).end_of_day}}])
354+
expect(@abstract_model.send(:filter_scope, FieldTest, 'datetime_field' => {'1' => {v: ['January 02, 2012 00:00'], o: 'default'}}).selector).to eq('$and' => [{'datetime_field' => {'$gte' => Time.local(2012, 1, 2), '$lte' => Time.local(2012, 1, 2).end_of_day}}])
355355
end
356356

357357
it 'supports enum type query' do

0 commit comments

Comments
 (0)