I am using 'sunspot_solr', '~> 2.0.0' and 'cancan', '~> 1.6.8' gems in my rails app but I can't do a successful search using those two, say I have a resource called Photos and this is my search query
photos = Photos.accessible_by(current_ability).search do
fulltext params[:query]
end.results
but the search happens on all photos not on those that belongs to current user, I believe current_user.photos and Photos.accessible_by(current_ability) are the same.
My ability.rb has this permissions
can :list, Photos
can [:read, :create, :update, :destroy], Photos, user_id: user.id
Any help would be much appreciated.
I don't think that the Sunspot search will filter based on a given scope, it just takes a model argument so it will search across all instances.
You could do the search first and then filter the results but that would mess up paging if you are using Sunspot to do that.
A better solution might be to index the user_id attribute in Solr so that you can do a search filtered by that as well as by the free text input. It isn't ideal because you would be duplicating authorisation logic.
So in your model you would need:
searchable do
...
integer :user_id
end
You would need to rebuild the search index.
And then include it in your search filter with something like:
photos = Photos.search do
fulltext params[:query]
with(:user_id).equal_to(current_ability.user.id)
end.results
There is a discussion of a similar problem here.
Even if I consider Steve answer correct you will have two different places in which you define permissions for the photos, and this is not nice because we are actually using cancan for that.
I would prefer using something like:
photos = Photo.search do
fulltext params[:query]
with(:id, Photo.accessible_by(current_ability).pluck(:id))
end.results
so you do not have to duplicate the logic for permissions.
btw: why Photos instead of Photo?
Related
I'm using sunspot_solr gem with my rails app and below is my method for searching users
users = customers_object.search do
fulltext params[:query]
end.results
but the search does not happen in my customers_object objects but it searches within the whole model, I could not find any resources on how to achieve this, is this possible ? if yes can anyone guide me how to do it ?
Thanks in advance
I'm not aware of such syntax
current_user.customers.search
Guess it just works, because the search method is defined on the Customer class.
You should index all user_ids within Customer Index, then pass the proper with scope.
class Customer
has_and_belongs_to_many :users
searchable do
integer :user_ids, multiple: true
....
end
end
Customer.search do
with :user_ids, current_user.id
fulltext param[:query]
end
finally found it, I was also using cancan gem and I was supossed to do User.accessible_by(current_ability) rather than current_users_customers_object, may help someone
I have a 'Sort By' dropdown on an events page where users can view a number of events and I'd like to allow users to Sort the events by Name (Alphebetical), Date (Created_At), and perhaps (Number of people attending hi/low).
How can I achieve this? I'm guessing with a default scope: order, Event.where(:name, ASC) for example but i'm not sure as I've never done this before.. Also how would I display/use the scope in the dropdown?
I recommed you the very-usefull gem has_scope:
https://github.com/plataformatec/has_scope
What you could do with it:
class Event < ActiveRecord::Base
scope ordered, lambda do |attribute, order|
return where("FALSE") unless self.attribute_names.include?(attribute.to_s)
order(attribute.to_sym, order.to_sym
end
class EventsController < ApplicationController
has_scope :ordered do |controller, scope, value|
scope.ordered(value, :desc)
end
# view
options = [['Name', 'name'], ['Date', 'date']]
select_tag("ordered", options_for_select(options, selected: params[:ordered]), onchange: "window.location.href = window.location.origin + window.location.pathname + '?ordered=' + $(this).val();"
I did not test the view, you might have to change the code a little bit. The javascript redirect does not support extra GET params!
Have a look at the RailsCasts
http://railscasts.com/episodes/240-search-sort-paginate-with-ajax
This also includes pagination and simple searches. However, you can take from it the part that you're looking for.
For active record you just need to append .order(symbol) to your active record query.
for alphabetic you could do.
Object.where(ARRGUMENT).order(:name)
or for all
Object.order(:name)
You don't need to use a scope for this it is over kill for something this simple.
If you want to create an interactive table then you might want to consider a Javascript solution. This is the one that has worked the best for me.
https://datatables.net/
I have used and currently use this gem with great success.
https://github.com/rweng/jquery-datatables-rails
I am trying out Tire gem for my Demo Rails app to implement ElasticSearch.
So, here are my models associations :-
A User belongs to many UserGroups. And Every UserGroup has many Post associated with it.
So, this is what I do to show all the posts for a User in posts_controller.rb
def index
#user_groups = current_user.user_groups
for group in #user_groups
for p in group.posts
#posts = #posts.to_a.push p
end
end
end
Now, I want to add search functionality to it. A User may be able to search for a Post from all the Post that are visible to him.
So, I have two questions which are connected to each other.
Q1. How, do I add the search functionality by using the Tire gem for the User so that the user may search from the Posts that are visible to him ?
Tire allows to directly search on a model using
#posts = Post.search(params[:query])
But, I want to search from an array.
Q2. And Secondly, Is my approach correct, by first storing the concerned Posts in an array and then use Tire to search from that array ?
I am designing a basic file manager (the Asset model) in the Active Admin gem. Each Asset HABTM Groups, and vice-versa.
In my active_admin Asset resource I have a filter where I want to be able to
select multiple groups to filter by, so I added:
filter :groups_id, :as => :check_boxes, :collection => proc {Group.all}
All of the groups show up as checkboxes as expected. However, if I have asset_1, asset_2 and I have group_1 assigned to asset_1 and asset_2, and group_2 to asset_2, when I
filter by both roles, asset_2 lists itself twice.
How can I restrict the filter to use only "distinct" or "unique" assets to be returned?
I also have another problem, which is that the filters are not working at all in any of my scopes.
A quick update on Will's answer. I'm running Rails 5.0 and ActiveAdmin 1.0, and clean_search_params returned an error. But this worked instead:
def apply_filtering(chain)
super
#search.result(distinct: true)
end
Thanks!
Active admin read indicates to add
distinct: true
to get unique results.
To apply that to active admin, I'm using doing that like this:
controller do
def apply_filtering(chain)
#search = chain.ransack clean_search_params params[:q]
#search.result(distinct: true)
end
end
has_and_belongs_to_many accepts a :uniq option which ensures that only uniq records will be returned. Setting this in your model should do the trick.
class MyModel
has_and_belongs_to_many :things, :uniq => true
end
... and, a quick addition Alex's answer:
If you want to do this for all controllers in your app, you can add this to an initializer (mine's called active_admin_patches.rb) -
# This guarantees that the results for a filtered #index page search do not appear more than once, on any #index page in the AA app
# TODO: THIS WILL PROBABLY FAIL WITH SOME FUTURE UPDATE, SO BE READY TO UPDATE IT FROM THE LATEST GEM SOURCE
module ActiveAdmin::ResourceController::DataAccess
# Applies any Ransack search methods to the currently scoped collection.
# Both `search` and `ransack` are provided, but we use `ransack` to prevent conflicts.
def apply_filtering(chain)
#search = chain.ransack(params[:q] || {})
# This is the original line
# #search.result
# This is the patch
#search.result(distinct: true)
end
end
I'm not sure why anybody wouldn't want this to be the default behavior, but there's probably a reason. Hmm, maybe for cases where a column of the index view is one of the non-distinct rows. Yeah, that must be it.
Also, there's bound to be a better way to patch this less intrusively, but I'm in a hurry. :-)
I've run into a small problem with some search-functionality for a Rails 3 website I'm developing. I have a simple Post model that looks like this:
class Post < ActiveRecord::Base
acts_as_taggable
end
I'm using acts_as_taggable_on to make adding tags to my posts a bit easier. When I have a post tagged 'rails' and I do the following, all works well:
#posts = Post.tagged_with("rails")
Thing is, I also want to search for the title of the post. When I have a post titled 'Hello world' and tagged 'rails', I want to be able to find this post by searching for 'hello' or 'rails'. So I want a LIKE statement for the title column in combination with the tagged_with method acts_as_taggable_on provides. The where scope doesn't work, because it uses AND instead of OR.
I hoped meta_search would fix the problem, but this isn't working for me. I tried several things. Here are two examples of what I tried:
#search = Post.search(:tagged_with_or_title_like => params[:search])
#search = Post.search(:title_like => params[:search], :tagged_with => params[:search])
It just doesn't recognize 'tagged_with'. It's my first time using meta_search, so I might be doing something wrong here. ;) I've read somewhere that searchlogic worked in combination with acts_as_taggable_on, but since it doesn't support Rails 3, I can't use that.
I hoped somebody here could help me with this problem. Anyone know how to combine acts_as_taggable_on with meta_search? Or maybe know a solution without meta_search? Also, if there's a better option for acts_as_taggable_on that works with meta_search I'd love to hear that too. :)
EDIT:
I got it working without using tagged_with or meta_search. It looked like this:
Post.select("DISTINCT posts.*").joins(:base_tags).where("posts.title LIKE ? OR tags.name = ?", "%"+params[:search]+"%", params[:search])
The query created was ridiculous, so I tried a different route: without acts_as_taggable_on and meta_search. I created a tags table myself and connected it to the posts with has_and_belongs_to_many. I won't have other tables that need to be connected to the tags, so this will do the trick for me. I created a scope for searching both the tags and title:
scope :search, lambda { |search| select("DISTINCT posts.*").joins(:tags).where("posts.title LIKE ? OR tags.name = ?", "%"+search+"%", search) }
Now I can do the following:
Post.search(params[:search])
It works great and the query is also quite nice. Still, if anyone knows a better way: please tell me. It could also be helpful for the people coming here via Google.
Post.metasearch({:title_or_tag_taggings_tag_name_contains => params[:search]})
enjoy
I think the solution you're looking into is using database VIEW as new model.
You can create view containing join tables Post and Tags.
Then you can search it, using meta_search without problems.