CanCanCan、Pagy、MeiliSearch,这3个gem怎么组合?

问题描述 投票:0回答:2

我正在开发一个具有 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
字符串中,并将所需的参数添加到索引中。但这会很复杂,并且会增加索引大小。你有什么想法吗?

ruby-on-rails ruby cancancan meilisearch pagy
2个回答
0
投票
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
    

0
投票

感谢@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

© www.soinside.com 2019 - 2024. All rights reserved.