I have this code:
def self.by_vibe(vibe_id)
self.joins(:vibes).where(vibes: {id: vibe_id})
end
This code repeats (with some differences) all over my model, for example:
def self.by_music(music_id)
self.joins(:musics).where(musics: {id: music_id})
end
I have four or five of these, and thought of making just one and came out with something like this (for reference, hash contains {:vibes=>2}):
def self.by(hash)
self.joins(hash.keys.first).where(hash.keys.first.to_s => id = hash.values.first)
end
My self.by_vibe makes this query:
SELECT "bcls".* FROM "bcls" INNER JOIN "bcls_vibes" ON "bcls_vibes"."bcl_id" = "bcls"."id" INNER JOIN "vibes" ON "vibes"."id" = "bcls_vibes"."vibe_id" WHERE "vibes"."id" = $1 [["id", 2]]
And my second approach makes this:
SELECT "bcls".* FROM "bcls" INNER JOIN "bcls_vibes" ON "bcls_vibes"."bcl_id" = "bcls"."id" INNER JOIN "vibes" ON "vibes"."id" = "bcls_vibes"."vibe_id" WHERE "bcls"."vibe_id" = 2
How can I fix that where clause? Notice that the first one (the correct one is WHERE "vibes"."id" = $1 [["id", 2]] and the wrong one is WHERE "bcls"."vibe_id" = 2. It's asking for a different table.
You can get the first value as well as the first key:
def self.by(hash)
self.joins(hash.keys.first).where(
{ hash.keys.first.to_s => { id: hash.values.first } }
)
end
If you're only accepting one key value pair in your hash though it might be better to have two arguments to by:
def self.by(assoc, obj_id)
self.joins(assoc).where(assoc => { id: obj_id })
end
I just found the proper way of doing what I was trying to do.
def self.by(hash)
self.joins(hash.keys.first).where(hash.keys.first.to_s => {id: hash.values.first})
end
Related
I have a working code that filter out one row of if the conditions are met
ransacker :custome_search, formatter: proc { |v|
user = Util.new(param: v.strip).user. # gets the data from an external service
id = user.present? ? user.id : nil
id = id.presence
} do |parent|
parent.table[:id]
end
and a filter in my index page as
filter :custome_search, label: 'Custom Data Search:', filters: ['equals']
this work fine, but now I have to change this to accommodate multiple tuples
so I changed my code to
ransacker :custome_search, formatter: proc { |v|
users = Util.new(param: v.strip).find_users
ids = users.present? ? users.pluck(:id) : nil # now returns an array instead of just one id [1,2,3]
ids = ids.presence
} do |parent|
parent.table[:id]
end
the above code displays zero results as the query formed internally comes out to be
SELECT COUNT(*) FROM (SELECT 1 AS one FROM "users" WHERE "users"."id" = NULL LIMIT $1 OFFSET $2) subquery_for_count /*controller:users,action:index*/ [["LIMIT", 30], ["OFFSET", 0]]
but if I do a
ids=ids.first
the same will display one row,
how to I change my code to display multiple rows on the index page
I am using active admin 2.0.0
Well the ransack code was fine, I just needed to change the filter on the index page , any of these would work
filter :custome_search, label: 'Custom Data Search:', filters: ['in']
or
filter :custome_search_in, label: 'Custom Data Search:', as: :string
I have a Rails query which is shown below:
query_results =
User.
joins("INNER JOIN posts ON posts.user_id = users.user_id").
select("posts.topic, posts.thread_id")
query_results contains values of 2 columns: topic and thread_id.
I would like to split query_results into 2 arrays - 1 containing values from all records (from query_results) for column topic alone and the 2nd containing values from all records for column thread_id alone.
How can I achieve this?
Try This out can help you!
here we are going to use pluck.
Yes. According to Rails guides, pluck directly converts a database result into an array, without constructing ActiveRecord objects. This means better performance for a large or often-running query.
topic_arr = []
thread_id = []
query_results = User.joins("INNER JOIN posts ON posts.user_id = users.user_id").pluck("posts.topic, posts.thread_id")
query_results.each do |i|
topic_arr.push(i.first)
thread_id.push(i.last)
end
puts query_results #=>[["topic1", 1], ["topic2", 2], ["topic3", 3]]
puts topic_arr #=>["topic1","topic2","topic3"]
puts thread_id #=>[1,2,3]
I think you can try below code for your requirement :-
query_results =
User.
joins("INNER JOIN posts ON posts.user_id = users.user_id").
pluck("posts.topic, posts.thread_id").to_h
topic_arr = query_results.keys
thread_id_arr = query_results.values
Example
Above query will give you result like:-
query_results = {"topic 1"=>1, "topic 2" => 2}
topic_arr = query_results.keys
topic_arr = ["topic 1", "topic 2"]
thread_id_arr = query_results.values
thread_id_arr = [1, 2]
I have a model Category that has_many Pendencies. I would like to create a scope that order the categories by the amount of Pendencies that has active = true without excluding active = false.
What I have so far is:
scope :order_by_pendencies, -> { left_joins(:pendencies).group(:id).order('COUNT(pendencies.id) DESC')}
This will order it by number of pendencies, but I want to order by pendencies that has active = true.
Another try was:
scope :order_by_pendencies, -> { left_joins(:pendencies).group(:id).where('pendencies.active = ?', true).order('COUNT(pendencies.id) DESC')}
This will order by number of pendencies that has pendencies.active = true, but will exclude the pendencies.active = false.
Thank you for your help.
I guess you want to sort by the amount of active pendencies without ignoring categories that have no active pendencies.
That would be something like:
scope :order_by_pendencies, -> {
active_count_q = Pendency.
group(:category_id).
where(active: true).
select(:category_id, "COUNT(*) AS count")
joins("LEFT JOIN (#{active_count_q.to_sql}) AS ac ON ac.category_id = id").
order("ac.count DESC")
}
The equivalent SQL query:
SELECT *, ac.count
FROM categories
LEFT JOIN (
SELECT category_id, COUNT(*) AS count
FROM pendencies
GROUP BY category_id
WHERE active = true
) AS ac ON ac.category_id = id
ORDER BY ac.count DESC
Note that if there are no active pendencies for a category, the count will be null and will be added to the end of the list.
A similar subquery could be added to sort additionally by the total amount of pendencies...
C# answer as requested:
method() {
....OrderBy((category) => category.Count(pendencies.Where((pendency) => pendency.Active))
}
Or in straight SQL:
SELECT category.id, ..., ActivePendnecies
FROM (SELECT category.id, ..., count(pendency) ActivePendnecies
FROM category
LEFT JOIN pendency ON category.id = pendency.id AND pendnecy.Active = 1
GROUP BY category.id, ...) P
ORDER BY ActivePendnecies;
We have to output ActivePendnecies in SQL even if the code will throw it out because otherwise the optimizer is within its rights to throw out the ORDER BY.
For now I developed the following (it's working, but I believe that it's not the best way):
scope :order_by_pendencies, -> { scoped = Category.left_joins(:pendencies)
.group(:id)
.order('COUNT(pendencies.id) DESC')
.where('pendencies.active = ?', true)
all = Category.all
(scoped + all).uniq}
I have two queries, I need an or between them, i.e. I want results that are returned by either the first or the second query.
First query is a simple where() which gets all available items.
#items = #items.where(available: true)
Second includes a join() and gives the current user's items.
#items =
#items
.joins(:orders)
.where(orders: { user_id: current_user.id})
I tried to combine these with Rails' or() method in various forms, including:
#items =
#items
.joins(:orders)
.where(orders: { user_id: current_user.id})
.or(
#items
.joins(:orders)
.where(available: true)
)
But I keep running into this error and I'm not sure how to fix it.
Relation passed to #or must be structurally compatible. Incompatible values: [:references]
There is a known issue about it on Github.
According to this comment you might want to override the structurally_incompatible_values_for_or to overcome the issue:
def structurally_incompatible_values_for_or(other)
Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
(Relation::MULTI_VALUE_METHODS - [:eager_load, :references, :extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
(Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
end
Also there is always an option to use SQL:
#items
.joins(:orders)
.where("orders.user_id = ? OR items.available = true", current_user.id)
You can write the query in this good old way to avoid error
#items = #items.joins(:orders).where("items.available = ? OR orders.user_id = ?", true, current_user.id)
Hope that helps!
Hacky workaround: do all your .joins after the .or. This hides the offending .joins from the checker. That is, convert the code in the original question to...
#items =
#items
.where(orders: { user_id: current_user.id})
.or(
#items
.where(available: true)
)
.joins(:orders) # sneaky, but works! 😈
More generally, the following two lines will both fail
A.joins(:b).where(bs: b_query).or(A.where(query)) # error! 😞
A.where(query).or(A.joins(:b).where(bs: b_query)) # error! 😞
but rearrange as follows, and you can evade the checker:
A.where(query).or(A.where(bs: b_query)).joins(:b) # works 😈
This works because all the checking happens inside the .or() method. It's blissfully unaware of shennanigans on its downstream results.
One downside of course is it doesn't read as nicely.
I ran into the same issue, however the code was defined in a different place and was very difficult to change directly.
# I can't change "p"
p = Post.where('1 = 1').distinct # this could also be a join
And I needed to add an or statement to it
p.or(Post.where('2 = 2'))
The following code won't raise an error, because it has distinct like the initial relationship.
p.or(Post.where('2 = 2').distinct)
The problem with it it that it only works as long as you know the relationship. It may or not have a join, or distinct.
This works regardless of what the relationship is:
p.or(p.unscope(:where).where('2 = 2'))
=> SELECT DISTINCT `posts`.* FROM `posts` WHERE ((1 = 1) OR (2 = 2))
It occurs when you try to combine two multi-active records of the same type, but one of them has a joins value or an includes value, or in your case a reference value, that the other does not.
Therefore we need to match the values between them, and I found a general way to do this without knowing the actual values in advance.
items_1 = #items.joins(:orders)
.where(orders: { user_id: current_user.id})
items_2 = #items.where(available: true)
.joins(items_1.joins_values)
.includes(items_1.includes_values)
.references(items_1.references_values)
#items = items_1.or(items_2)
just solve it!
def exec_or_statement(q1, q2)
klass = q1.klass
key = klass.primary_key
query_wrapper_1 = {}
query_wrapper_1[key] = q1
query_wrapper_2 = {}
query_wrapper_2[key] = q2
klass.where(query_wrapper_1).or(klass.where(query_wrapper_2))
end
query_1 = #items.where(available: true)
query_2 =
#items
.joins(:orders)
.where(orders: { user_id: current_user.id})
exec_or_statement(query_1, query_2)
i am using postgres and wishing to exclude users that are currently in one table from another. at present i am trying do this via the ActiveRecord system within Rails.
So i need it to get the ids from my Availability table, then return that id into my User table to remove them if they are in the Availability table.
#availabilities = Availability.where(:event_id => params[:id]).all
#players = User.where('team_id = ? and id <> ?', current_user[:team_id], #availabilities).all
this is returning the following error
PG::Error: ERROR: argument of WHERE must be type boolean, not type record
LINE 1: SELECT "users".* FROM "users" WHERE (team_id = 1 and id <> ...
^
: SELECT "users".* FROM "users" WHERE (team_id = 1 and id <> 101,102,103)
changed code as mentioned below, though the way i am doing it is still probably not ideal
#availabilities = Availability.where(:event_id => params[:id]).all
#exclude = Availability.where(:event_id => params[:id]).select(:user_id).pluck(:user_id)
if #exclude.count > 0
#players = User.where('team_id = ? and id NOT IN (?)', current_user[:team_id], #exclude).all
else
#players = User.where('team_id =?', current_user[:team_id])
You could do something like this:
#availabilities = Availability.where(event_id: params[:id]).pluck(:id)
#players = User.where(team_id: current_user[:team_id])
#players = #players.where('id NOT IN (?)', #availabilities) unless #availabilities.empty?
Using pluck() will return an array of IDs, then you can exclude them by using NOT IN (?)
Try:
id not in
The way pg engine sees it is ((team_id=1 and id <> 101), 102, 103). Thus the error you see.
Use it as:
User.where('team_id = ? and id not in (?)', current_user[:team_id], #availabilities).all