Picky Case Study: Restricting Results Tweet
This is a post in the Picky series on its workings.
Intro
Recently a Picky user contacted me with an intriguing question. Items have restricted visibility. Some items can only be seen by Mr. Black (user id 5), but others only by Mr. Pink (user id 42). All items can each only be seen by a small number of users.
The question: “How can we do it?”
It turns out, Picky can do this already quite easily.
Here goes
Let’s say we have items that have a method #restricted_to_user_ids
that returns an array of user ids which can “see” this item in results:
class Item
attr_reader :id # e.g. 42
attr_reader :name # e.g. "Dan"
attr_reader :restricted_to_user_ids # e.g. [2,3,5,7,11]
end
Quite nice.
But how can we ask Picky to just return results that the current user can see?
Since Picky is good at filtering, we could prefix each query by, say,
restricted:5
which would create queries like
restricted:5 my cool query
(how we do this we’ll see later). This means we’d only search for items which have 5 in their restricted user ids list.
Now. Since Picky cannot yet directly index the array returned by #restricted_to_user_ids
, we have to use a technique, which in german would be called “from behind through the breast into the eye”:
We create a reader, which simply joins the array from #restricted_to_user_ids
into a string with space-separated user id values.
class Item
attr_reader :id # e.g. 42
attr_reader :name # e.g. "Dan"
attr_reader :restricted_to_user_ids # e.g. [2,3,5,7,11]
def restricted
restricted_to_user_ids.join(' ') # e.g. "2 3 5 7 11"
end
end
Assuming we split the data on spaces, Picky indexes the ids nicely for each item.
Then, all we have to do is add the category :restricted
(which uses the reader we just defined) to the index.
items = Picky::Index.new :items do
source { Item.order('name DESC') }
indexing splits_text_on: /\s/
category :name
category :restricted
end
The JS frontend
Finally, to add the restricted:<user_id>
text in front of each query, we use the Javascript callback available in the generated client, before
. Since version 3.1.2, before gets the query and the params.
Whatever you return is used as the new query.
before: function(query, params) { return query.replace(/^/, 'restricted:' + user_id + ' ') }
This code replaces "my beautiful query" => "restricted:5 my beautiful query"
(Please note that the JS function #replace
leaves the original string alone).
One little problem
Did you notice? There’s one little problem with solving it in JavaScript.
If the visibility restriction is not crucial, but only helpful to your users, we would be finished.
However, if Mr. Pink cannot ever see results that only Mr. Black should have access to, we’d now have a big problem.
The solution?
The solution is to route the full and live requests through our web server, and adding the restricted:<user_id>
there. So in the server you’d prepend your query with "restricted:#{current_user.id} #{params[:query]}"
and send it off to the Picky server.
And that’s it already. Nobody loses an ear. Quite easy, don’t you think?
Next Picky Case Study: Location Based AdsShare
Previous Migrating to Picky 3.1 (from 3.0)