Rails subquery always returns nil value - ruby-on-rails

I need to count rows from other table in subselect, so I use this query:
follows_sql = Follow.where(followable_type: 'Idea').where('follows.followable_id = ideas.id').select('COUNT(followable_id)').to_sql
idea = Idea.select("(#{follows_sql}) AS fcnt").includes(:collaborations).
where(collaborations: { user_id: 4, owner: true })
So its produced valid SQL, but I cant access 'fcnt' value from idea var. I tried do it in different ways like:
idea[0].fcnt # return nil
idea[0]["fcnt"] # return nil
But I only can access fields which exists in a Idea model.
How I can access my custom 'fcnt' field?

I think something along the following should work for you
idea = Idea.select("ideas.*, COUNT(follows.id) AS fcnt").joins("LEFT OUTER JOIN follows ON follows.followable_id = ideas.id").group("ideas.id")
ideas.each do |idea|
puts idea.fcnt
# Should output a number
end
Note that I've left out the other includes and where clauses. Try to solve the problem first, and if this query works out, then add in your additional clauses.
Also, if you setup your relations correctly, such that an idea has many follows, you could clean up your code by doing something like
ideas = Idea.includes(:collaborations).where(collaborations: { user_id: 4, owner: true })
ideas.map { |idea| idea.follows.count }

Related

Looping over Ruby hash and accessing values

I am quite new to Ruby and could not find an appropriate answer to my questions. Let's say I have hash named
users_hsh = {}.
I am looping through all of my users in the DB and creating the following.
users.each do |user|
users_hsh[user.full_name] = {
completed_activities: some_integer_value,
active_activities: some_integer_value,
future_activities: some_integer_value
}
end
Now, I created a new hash named
total_sum_not_zero_user_hsh = {}.
I want to loop over all of the users in the users_hsh and check for each user if the total sum of completed_activities + active_activities + future_activities does not equal 0 and if this condition holds, I want to add this user to total_sum_not_zero_user_hsh. I have done the following but seems that this does not work.
users_hsh.each do |usr|
if usr.values.sum != 0
total_sum_not_zero_user_hsh[usr] = {
completed_activities: some_integer_value,
active_activities: some_integer_value,
future_activities: some_integer_value
}
end
end
What am I doing wrong? Thanks in advance!
Let's use your example of:
users_hash = {
"Elvin Jafarli" => {
completed_activities: 10,
active_activities: 2,
future_activities: 0
}
}
Think carefully about what your data structure actually is: It's a hash that maps the user name to some user attributes. If you loop through these values, you don't just get a usr, you get back precisely this mapping.
It's helpful to name your variables descriptively:
users_hsh.each do |user_name, user_attributes|
if user_attributes.values.sum != 0
# ...
end
end
With your attempt, you would have seen an error like this: NoMethodError: undefined method 'values' for #<Array:0x00007fe14e22f538>. What happened is that each usr was actually an Array such as:
["Elvin Jafarli", {completed_activities: 10, active_activities: 2, future_activities: 0}]

Relation passed to #or must be structurally compatible. Incompatible values: [:references]

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)

Select certain record within response from ActiveRecord

I have a call to ActiveRecord in my controller as so:
#configurations = EmailConfiguration.where(customer_id: '1', email_template: '1')
This will return all EmailConfigurations that have the correct parameters. Each record has a field_id and a the_value. I want to display the value in the view:
#configurations.where(field_id: 1).the_value
What do I need to add to the view to select a certain record within the collection that is returned by the database?
You can use select for a quick filter on arrays
#configurations.select {|c| c.field_id == 1}
that will return all collections with field_id = 1. If you know there is only one, you could chain it for a direct output:
#configurations.select {|c| c.field_id == 1}.first.the_value
#configurations.where(field_id: 1)
returns a collection of objects(array) even if there is only one result. If you would like to show only one you can do as suggested above:
#configurations.select {|c| c.field_id == 1}.first.the_value
If you want to show all of the "the_values" you can do
field_1_configs = #configurations.select do |c| c.field_id == 1
end
field_1_configs.map{|config| config.the_value }

Check if string contains element in Array

I'm using Rails and learning ActiveRecord and I came across a vexing problem. Here's an array in my model:
#sea_countries = ['Singapore','Malaysia','Indonesia', 'Vietnam', 'Philippines', 'Thailand']
And here's my ActiveRecord object:
#sea_funding = StartupFunding.joins(:startup)
.where('startups.locations LIKE ?', '%Singapore%')
What I'm trying to do is to return a result where a string in the 'locations' column matches any element in the Array. I'm able to match the strings to each element of an Array (as above), but I'm not sure how to iterate over the whole Array such that the element is included as long as there's one match.
The intent is that an element with multiple locations 'Singapore,Malaysia' would be included within #sea_funding as well.
Well, don't ask me why 'locations' is set as a string. It's just the way the previous developer did it.
You use an IN clause in your .where filter:
#sea_funding = StartupFunding.joins(:startup)
.where(["startups.locations IN (?)", #sea_countries])
#sea_countries.include?(startups.locations)
This will return a boolean TRUE if the value of the locations column in startups can be found in the sea_countries array, false if it is absent.
Could this work for you?
first = true
where_clause = nil
sea_countries.each do |country|
quoted_country = ActiveRecord::Base.connection.quote_string(country)
if first
where_clause = "startups.locations LIKE '%#{quoted_country}%' "
first = false
else
where_clause += "OR startups.locations LIKE '%#{quoted_country}%' "
end
end
#sea_funding = StartupFunding.joins(:startup)
.where(where_clause)

retrieve data from database - hash

I have a table called audits which has a column 'changes' storing data in the form of hash
I would like to retrieve all entries with the following conditions:
- auditable_type = 'Expression'
- action = 'destroy'
- changes = { :EXP_SUBMISSION_FK =>'9999992642'}
I first tried the following code which returns me with nothing:
#deleted_history = Audit.find(:all, :conditions => ["auditable_type =? AND action = ? AND changes = ?",'Expression', 'destroy' , { :EXP_SUBMISSION_FK =>'9999992642'} ])
I then tried the following code which retrieves all entries in the 'audits' table with auditable_type = 'Expression' and action = 'destroy'.
I then loop through the resultset and discards all entries where EXP_SUBMISSION_FK is not equal to 9999992642. The code below returns me 5 entries/records
#deleted_history = Audit.find(:all, :conditions => ["auditable_type =? AND action = ?",'Expression', 'destroy' ])
#deleted_history.each do |test|
if test.changes['EXP_SUBMISSION_FK'] != 9999992642
#deleted_history = #deleted_history.reject { test }
end
end
I would like to know where did I go wrong with the first code example and whether there is a way of retrieving all entries with the aforementioned conditions in a much simpler way.
Thanks a lot for your help.
i'd do:
#deleted_history.select!{|hist| hist.changes['EXP_SUBMISSION_FK'] == '9999992642'}
One potential cause of failure is that you're looking for 9999992642 but you state before the value is '9999992642'
You just use something like below. I am storing element_values as a hash and i am selecting records based on the key/value pair.
scope :find_by_field_values, lambda {
|field_name, field_value|
(where("element_values like ?", "%\"#{field_name}\":\"%#{field_value}%"))
}
just try this based on your scenario.

Resources