我正在开发一个具有 Cue 模型的项目,该模型可以由访客和管理员等多个角色访问和搜索。授权是基于 Ability 类中编写的一些逻辑使用 CanCanCan gem 进行的。搜索使用 Pagy/MeiliSearch 组合进行全文搜索和分页。我现在面临的问题是,当我以访客身份搜索时,我可以看到访客应该看到的正确项目,而当我以管理员身份搜索时,我可以看到管理员应该看到的正确项目,但是 Pagy 宝石数量是不正确。这是我的代码:
class SearchesController < ApplicationController
INCLUDES = %i[medium speakers].freeze
include Pagy::Backend
authorize_resource class: false
def search
filter = ''
filter = "speaker_ids IN [#{params[:speaker]}]" if params[:speaker].present?
search_results = Cue.accessible_by(current_ability).includes(INCLUDES).pagy_search(params[:query], filter:)
@pagy, @search_results = pagy_meilisearch(search_results)
end
end
如果客人无法访问某些提示,搜索将不会在
@search_results
中返回它们,但 @pagy
对象会将它们计入。这会向用户显示不正确的计数,也显示少于所需的 Pagy::DEFAULT[:items]
.我尝试了很多选择,但没有成功。我唯一能想到的(我不想走这条路 TBH)是将过滤逻辑本身添加到 filter
字符串中,并将所需的参数添加到索引中。但这会很复杂,并且会增加索引大小。你有什么想法吗?
just hint
在meilisearch中搜索时不直接读取CanCan中的当前能力, 可以在application_record中处理 获取所有 CanCan 能力并覆盖 https://github.com/meilisearch/meilisearch-rails/blob/5d4ac6871a8e89db06fdd1c6d765361b86c47eb6/playground/config/initializers/pagy.rb#L10
如你所见,我们不叫罐罐
可以在
app/models/application_recored.rb
def ms_search(query, params = {})
# cancan_params should here merge cancan_params and get params
# to search with cancan params
params.merge!(cancan_params)
super(query, params)
end
感谢@abdelrahman-haider 的提示,很有用。
基本上,我们无法解决这个问题,除非我们在美丽搜索索引本身内部实现过滤逻辑。所以,我所做的是使用 CanCan 能力提取过滤条件,并手动将其转换为 MeiliSearch 过滤字符串。
要获取特定模型在特定控制器动作上的 CanCan 能力条件,我们可以执行以下操作:
cue_conditions = current_ability.model_adapter(Cue, :show).conditions
然后,我们可以将
cue_conditions
传递给另一个方法将它们转换为MeiliSearch过滤字符串,如下所示:
def ability_conditions_to_meilisearch_filter(condition)
condition.map do |key, value|
case value
when Array then "#{key} IN [#{value.join(',')}]"
when NilClass then "#{key} NOT EXISTS"
when TrueClass, FalseClass then "#{key} = #{value}"
when Hash then hash_condition_to_filter(key, value)
else "#{key} = '#{value}'"
end
end.join(' AND ')
end
def hash_condition_to_filter(key, value)
case value.keys[0]
when :gt then "#{key} > #{value[:gt]}"
when :gte then "#{key} >= #{value[:gte]}"
when :lt then "#{key} < #{value[:lt]}"
when :lte then "#{key} <= #{value[:lte]}"
when :to then "#{key} #{value[:to].first} TO #{key} #{value[:to].last}"
end
end
ability_conditions_to_meilisearch_filter
方法的实现不是最好的实现,但是暂时满足了我的要求,以后会努力完善
请注意,您需要将所需的属性添加到您的 MeiliSearch 索引中。
我希望这对您有所帮助,因为我在互联网上没有找到任何相关内容:) 如果您想支持,我向
meilisearch-rails
GitHub 存储库提交了一个功能请求:https://github.com/meilisearch/meilisearch -rails/issues/255.
最后,感谢@abdelrahman-haider 和 poe.com 的帮助:3