-->

Cancan Thinking Sphinx current_ability Questions

2019-04-02 06:43发布

问题:

trying to get cancan working with thinking sphinx but running into some issues.

Before using sphinx, I had this in my companies view:

@companies = Company.accessible_by(current_ability)

That prevented my users from seeing anyone else's companies...

After installing sphinx, I ended up with:

  @companies = Company.accessible_by(current_ability).search(params[:search], :include => :order, :match_mode => :extended ).paginate(:page => params[:page])

Which now displays all my companies and isn't refining per user based on ability.

It would see ts isn't set up for cancan?

回答1:

I think it's more that accessible_by is probably a scope - which is Database/SQL-driven. Sphinx has its own query interface, and so ActiveRecord scopes don't apply.

An inefficient workaround (gets all companies first):

company_ids = Company.accessible_by(current_ability).collect &:id
@companies  = Company.search params[:search],
  :include    => :order,
  :match_mode => :extended,
  :page       => params[:page],
  :with       => {:sphinx_internal_id => company_ids}

A couple of things to note: sphinx_internal_id is the indexed model's primary key - Sphinx has its own unique identifier named id, hence the distinction. Also: You don't want to call paginate on a search collection - Sphinx always paginates, so just pass the :page param through to the search call.

There'd be two better workarounds that I can think of - either have a Sphinx equivalent of accessible_by, with the relevant information added to your indices as attributes - or, simpler if not quite as ideal, just get the company ids returned in the first line of my above snippet without loading up every company as an ActiveRecord object. Both will probably mean bypassing and/or duplicating Cancan's helpers.

Although... maybe this would do the trick, taking the latter approach:

sql         = Company.accessible_by(current_ability).select(:id).to_sql
company_ids = Company.connection.select_values sql
@companies  = Company.search params[:search],
  :include    => :order,
  :match_mode => :extended,
  :page       => params[:page],
  :with       => {:sphinx_internal_id => company_ids}

Avoids loading unnecessary Company objects, uses the Cancan helper (provided it is/returns a scope), and works neatly with what Sphinx/Thinking Sphinx expects. I've not used Cancan though, so this is a bit of guesswork.