2

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?

Jenny Blunt
  • 1,576
  • 1
  • 18
  • 41

1 Answers1

3

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.

pat
  • 16,116
  • 5
  • 40
  • 46
  • Wow, slightly more complex than I'd hoped... Let me digest your approach - really appreciate you taking the time to answer – Jenny Blunt Jul 11 '11 at 21:59
  • It's also worth noting Ryan Bates (author of Cancan) has confirmed that accessible_by is a scope, so the final solution should do the trick. Any questions, let me know :) – pat Jul 12 '11 at 00:38
  • Is there a way to pass sql directly to thinking_sphinx? This solution is awesome, but it seems like it would have trouble scaling, if the company_ids array got large. – Cyrus Mar 03 '12 at 00:37
  • Sphinx doesn't understand SQL when it's dealing with searches - it only talks to the database when indexing - so giving it a SQL query as part of a search won't work. – pat Mar 04 '12 at 14:45