给activeadmin的filter添加自定义搜索条件

背景

默认情况下,如果我们定义一个activeadmin的filter。

1
filter :school_name, as: :string, label: "学校名称"

我们就可以按照school_name进行搜索。相应的SQL的mapping就会变成

1
2
3
...
where school_name like "%school_name%"
...

然而,很多时候我们想输入多个空格隔开的关键词,比如学校名清华大学 北京大学这种来匹配任何学校名包含这些学校的记录。

那么这个filter如何自定义呢?

自定义filter的搜索条件

首先在model中新建一个自定义的scope。

1
2
3
4
5
6
7
8
9
10
scope :school_name_includes, ->(search) {
current_scope = self
query = ""
search.split.uniq.each do |word|
query += "school_name LIKE '%#{word}%' or "
end
query=query[0..-4]
current_scope = current_scope.where(query)
current_scope
}

search是我们获取到的输入的搜索条件。比如上面的清华 北大
我们首先将单词分开,之后逐个加入LIKE搜索条件,并用or隔开。
去掉末尾多余的or之后,加入当前的scope条件中去。
这样,当我们输入清华 北大的时候,对应的SQL就会变成

1
where school_naame like "%清华%" or school_name like "%北大%"

最后,为了使得activeadmin能找到这个filter,我们需要定义。

1
2
3
def self.ransackable_scopes(auth_object = nil)
[ :school_name_includes]
end

以上,我们就可以在DSL中使用

1
filter :school_name_includes, as: :string, label: "学校名称"

来匹配符合空格隔开的多个学校中的任意一个的记录了。

后话

一开始总想着往current_scope上加or条件,后来发现,其实query里面,我们自己嵌入就行了。
总而言之,了解自己能够更改的范围以及在这个范围内思考问题很是重要。问题卡顿的时候,可以再三阅读文档,了解接口的特征,反复思考需求。