I have 2 scopes, the by_group_title does is used in a search box to return group names based off of a user search. It's working good, however, I'm trying to limit what groups can be returned in the search results. Specifically I'm trying to exclude any group that has a "membership" of "Restricted", I only want groups returned in the search results when the "membership" is set to "Standard" for the group.
The second scope I have is called by_standard_membership. This scope will return standard groups but it does not allow searching. So I'm trying to find a way to combine both scopes so that a user can search for a group title but only the standard groups are shown and the restricted groups do not appear in the search results.
I've been playing around with using an "and" statement to combine them but can't seem to get it working.
#scopes
scope :by_group_title, -> (group) { where('title LIKE ?',"%#{group}%" ).order(created_at: :desc) if group.present? }
scope :by_standard_membership, -> { where(membership: "Standard").order(created_at: :desc) }
There are a few ways you can do this. In the examples below, I have added the condition(AND) someNum = 1 to the expressions you provided.
You can either add AND to the where clause ...
scope :by_group_title, -> (group) { where('title LIKE ? and someNum = 1',"%#{group}%" ).order(created_at: :desc) if group.present? }
Or use the ActiveRecord fields ...
scope :by_standard_membership, -> { where(membership: "Standard", someNum: 1).order(created_at: :desc) }
Alternatively, you can chain the where clauses together ...
scope :by_standard_membership, -> { where(membership: "Standard").where(someNum: 1).order(created_at: :desc) }
Related
I have a model Deployment, it has following relations:
belongs_to :user
belongs_to :user_group, optional: true
I want to be able to define scope for easier filtering. I can easily filter by user, something like this:
scope :by_owner, -> (ow) { joins(:user).where("users.name like ?", "%#{ow}%") }
But what I really want to use user_group first if association exists and if it doesn't, then fall back on user. Logic would work like this:
If this deployment belongs to at least one user_group
scope :by_owner, -> (ow) { joins(:user_group).where("user_groups.name like ?", "%#{ow}%") }
else
scope :by_owner, -> (ow) { joins(:user).where("users.name like ?", "%#{ow}%") }
end
I am guessing that I have to achieve it somehow with an SQL query, but I can't seem to figure it out. Any suggestions?
Thanks!
Update
I got a bit closer with the following query:
scope :by_owner, -> (ow) { joins(:user, :user_group).where("(users.display_name like ?) OR (user_groups.name like ?)", "%#{ow}%", "%#{ow}%") }
But it's still not it because 1st of all the records that don't have user_groups association are ignored in the response and also it searches by user even if user_group is defined. Still looking for suggestions if there are any.
When using array or string conditions inside Rails query, for example:
Scope in Location model:
scope :name_like, ->(keyword) {where("name ilike ?", keyword)}
It will have problem when using it with join table who also has column name. It is like:
Location.joins(:users).name_like('main')
It will report ambiguous column name conflicts at name.
How should I address this issue, thanks!
Change your name_like scope to use explicit name of locations. I suggest to change it as below:
scope :name_like, -> (keyword) { where("locations.name ilike ?", keyword) }
You need to use like this
scope :by_name, -> { joins(:users).where("users.name like '%?%'",'FirstName' ) }
Refer this link below.
https://apidock.com/rails/ActiveRecord/NamedScope/ClassMethods/scope
If you need to pull all duplicates by name in the class you can achieve it by:
Company.select(:name).group(:name).having("count(*) > 1")
By what to do if you want it in the scope
scope :duplicates, -> { where (...?)}
Also in return I need few fields not only name. Did anyone had the same problem to create a scope?
You need to run this in two queries. The first query selects the duplicate names, the second one selects the records with those duplicate names and uses the current_scope so that it can be chained with more scopes if needed (unfortunately current_scope seems to be a very useful but undocumented method):
scope :duplicates,
-> {
dup_names = Company.group(:name).having("count(*) > 1").pluck(:name)
current_scope.where(name: dup_names)
}
(The dup_names variable will contain an array of duplicate names found among the companies.)
Then you can easily add further conditions on the duplicate records, for example:
Company.duplicates.where("name like 'a%'").limit(2)
will select just two companies with the name starting with 'a' (and with duplicate names).
Since
scope :red, -> { where(color: 'red') }
is simply 'syntactic sugar' for defining an actual class method:
class Shirt < ActiveRecord::Base
def self.red
where(color: 'red')
end
end
you could define scope like this:
scope :duplicates, -> { ids = select(:id).group(:name).having("count(name) > 1"); where(id: ids) }
I am building out a tag-based forum in Rails 4, where topics can be associated with tags.
class Topic < ActiveRecord::Base
...
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
...
scope :sort_by_latest_message, -> { order(latest_message_at: :desc) }
scope :sort_by_sticky, -> { order(sticky: :desc) }
...
scope :without_tags, -> { where.not(id: Tagging.select(:topic_id).uniq) }
scope :with_tags, -> { joins(:tags).where("tag_id IS NOT NULL").uniq }
...
end
Topics also have a boolean column called "sticky."
The issue: I want to be able to have topics to sort in a manner that places topics with the sticky property at the top of the list, but only if that topic has an association with at least one of a specified list of tags. Topics will then be sorted by the latest_message_at property.
This all occurs AFTER a filtering process.
So for example, the list of topics will contain topics with tags X, Y, and Z, but only sticky topics with tag X should truly be considered sticky, so any topics that have the sticky property but are associated with tags Y or Z instead of tag X should be sorted normally (by latest message). So ultimately, the list will have sticky topics under tag X at the top (sorted by latest message), then all other topics under tag X plus topics under tag Y and Z whether they are sticky or not, sorted by the latest_message_at property.
I currently have a setup like this:
def self.combine_sort_with_sticky(tag_ids, primary_sort)
if tag_ids.empty?
relevent_sticky_topics = without_tags.where(sticky: true)
other_topics = union_scope(*[with_tags, without_tags.where(sticky: false)]) # union_scope is a method that creates an SQL union based on the scopes within
else
relevent_sticky_topics = joins(:tags).where("tag_id IN (?)", tag_ids).uniq.where(sticky: true)
other_topics = joins(:tags).where("tag_id NOT IN (?) OR sticky = ?", tag_ids, false).uniq
end
combined_topics = relevent_sticky_topics.send(primary_sort) + other_topics.send(primary_sort) # Order is important, otherwise stickies will be at the bottom.
combined_topics.uniq
end
So when I call combine_sort_with_sticky([1], :sort_by_latest_message), only sticky topics with the tag of ID 1 AND the sticky property are moved to the front of the list. I'll also note that when not filtering on any tag, only topics without tags should be considered sticky.
This appears to give the results I want, but that + operator between the two sorted queries has me concerned, as it converts the ActiveRecord association to an Array object.
What I am looking for is a way to maintain the ActiveRecord association (such as a scope, or potentially another class model) while conditionally applying the first of the two sorting scopes. Topic.all.sort_by_sticky.sort_by_latest_message is close to what I want, but the problem is that it indiscriminately sorts by the sticky property, rather than only considering sticky topics with certain tags as true stickies.
I have been playing around with scopes like the following:
scope :sort_by_relevant_sticky, ->(tag_ids) { joins(:tags).order("CASE WHEN tag_id IN (?) THEN sticky ELSE latest_message_at END DESC", tag_ids).uniq }
but that doesn't seem to be working. I am not incredibly familiar with conditional SQL.
My database in Production is Postgresql.
This is not an actual solution for your problem, but I'd take a look at this: https://github.com/mbleigh/acts-as-taggable-on :)
If I do this:
#company.projects.joins(clients).select('projects.id', 'clients.name as client_name', 'budget_id', 'budget_visible')
I'm getting only the 3 attributes I'm looking for.
I have also declared the following scopes in project.rb:
scope :active, -> { where(active: true).includes(:client).where('clients.active' => true).references(:client) }
When I do:
#company.projects.active.select('projects.id', 'clients.name as client_name', 'budget_id', 'budget_visible')
I'm getting all attributes instead of the 3 I'm looking for.
What am I missing, and how can I select only the attributes I want?
Edit: another detail: when I do .to_sql I can see the SQL is perfect and only returns the columns I need, but for some reason when I do .first.attributes I see all the columns of Project instead of only the ones I need.