HQL Queries rounds off numbers - grails

I have built a grails application and am using HQL in my controller to pass parameters to my "index.gsp" using g-select tags. There is a very simple issue I am facing, when the value comes to the front-end (browser/client side), the numbers are rounded off.
I was not facing this before while using SQL in my controller, but now since my index.gsp and controller are communicating using "params" and g select, HQL had to be used and this rounds off all numbers (basic metrics, calculated metrics etc)
Query example (controller) :
In this query, I am taking revenue, it's YoY and WoW from my backend table and even this is rounded off ( all values shown in visualization)
def com = Com.executeQuery("
SELECT p.date_hour
,p.total_revenue
,CASE
WHEN total_revenue_ly IN (
0
,NULL
)
THEN 0
ELSE ((total_revenue / total_revenue_ly - 1) * 100)
END AS yoy
,CASE
WHEN total_revenue_lw IN (
0
,NULL
)
THEN 0
ELSE ((total_revenue / total_revenue_lw - 1) * 100)
END AS wow
FROM Com p
WHERE p.department = ?
AND p.device = ?
AND p.browser = ?
AND p.platform = ?
AND p.mv = ?
AND p.time_period = ?
ORDER BY col_0_0_ ASC",
[params.department, params.device, params.browser,
params.platform, params.mv, params.time_period])
render com as JSON
I also have to write queries for "conversion rate" etc(calculated metrics) :
def com = Tablev1.executeQuery("
SELECT p.date_hour
,CASE
WHEN visits IN ((0,NULL)
THEN 0
ELSE ((p.orders / p.visits) * 100)
END AS metric
,CASE
WHEN visits IN ((0,NULL)
THEN 0
WHEN orders_ly IN ((0,NULL)
THEN 0
WHEN visits_ly IN ((0,NULL)
THEN 0
ELSE ((((orders / visits) / (orders_ly / visits_ly)) - 1) * 100)
END AS yoy
,CASE
WHEN visits IN ((0,NULL)
THEN 0
WHEN orders_lw IN ((0,NULL)
THEN 0
WHEN visits_lw IN ((0,NULL)
THEN 0
ELSE ((((orders / visits) / (orders_lw / visits_lw)) - 1) * 100)
END AS wow
FROM Tablev1 p
WHERE p.platform = ?
AND p.mv = ?
AND p.time_period = ?
ORDER BY col_0_0_ ASC",
[params.platform, params.mv, params.time_period])
render com as JSON
Even these values are rounded off. I am displaying the values in the console of browser and in the array only they are rounded off, my visualization is a graph using highcharts.js but I don't think there is an issue with highcharts.js as the array feeded to highcharts is only rounded off.
The data type of revenue in first query was also float, but still it's rounded off.
The problem lies in HQL or index-controller communication
In a different application, using case when rounds off numbers and not using case when displays the decimals, I don't understand this. Can someone pls explain it?
How do I resolve this issue?
Any approches/suggestions are most welcome.
UPDATE:
Ignore the entire case when, a simple query like this also is passing rounded values
def com = Com.executeQuery("
SELECT p.date_hour
,p.total_revenue
FROM Com p
WHERE p.department = ?
AND p.device = ?
AND p.browser = ?
AND p.platform = ?
AND p.mv = ?
AND p.time_period = ?
ORDER BY col_0_0_ ASC",
[params.department, params.device, params.browser,
params.platform, params.mv, params.time_period])
render com as JSON

Try using a float value when multiplying:
... ELSE ((total_revenue / total_revenue_ly - 1) * 100.0)

Related

Rails, undefined method `total_pages' #<PG::Result:0x007f3ede39dae0>

Having issue while passing limit per paje display 50 records.
if params[:limit].present? && params[:offset].present?
offset_value = (params[:offset].to_i - 1) * params[:limit].to_i
limit << " LIMIT #{params[:limit].to_i} OFFSET #{offset_value}"
elsif params[:limit].present?
limit << " LIMIT #{params[:limit].to_i}"
else
if params[:offset].present?
offset_value = (params[:offset].to_i - 1) * 50
limit << " LIMIT 50 OFFSET #{offset_value}"
else
limit << " LIMIT 50"
end
end
query << "
select pp.id,
ud.last_name || ' ' || ud.first_name as full_name,
pp.image_file_name,
pp.gender_type ,
pp.no_of_view_for_last_30_days ,
pp.no_of_likes ,
pp.no_of_comments,
pp.tenpo_name_display,
pp.online_open ,
pp.online_comment from product pp
inner join user_details ud on pp.user_id = ud.user_id
inner join user_labels ul on ul.user_id = ud.user_id
where pp.flag = false and end_dt is null"
#posts = ActiveRecord::Base.connection.execute(query + limit)
having error in view, below line.
<%= will_paginate #posts, :renderer => BootstrapPagination::Rails %>
trying to make pagination, but having error like total_pages after passing objet to view.
It seems like you're trying to use will_paginate gem, aren't you?
If that's the case, you're doing too much in your controller. will_paginate is supposed to do all those calculations (offsets etc.) for you:
#posts = Post.paginate(page: params[:page])
Please note also that https://github.com/bootstrap-ruby/will_paginate-bootstrap (which it seems you're trying to use) is no longer maintained.
I done with this. get total count from query.
query << " select count(*) OVER() AS total_count,
pp.id,
ud.last_name || ' ' || ud.first_name as full_name,
pp.image_file_name,
pp.gender_type ,
pp.no_of_view_for_last_30_days ,
pp.no_of_likes ,
pp.no_of_comments,
pp.tenpo_name_display,
pp.online_open ,
pp.online_comment from product pp
inner join user_details ud on pp.user_id = ud.user_id
inner join user_labels ul on ul.user_id = ud.user_id
where pp.flag = false and end_dt is null"
offset = params[:page].present? ? (params[:page].to_i - 1) * 30 : 0
#posts = ActiveRecord::Base.connection.execute(query + 'limit 30 offset ' + offset.to_s )
posts_count = !#posts.nil? ? #posts.first["total_count"] : 0
#post_count = #posts.to_a.paginate(page: params[:page], per_page: 30, total_entries: posts_count)
passing param as total_entries to pagination. It's work.

Rails find_by_sql returning array with empty object

I am using find_by_sql to do a query on my Conversationalist model using Postgres as the DB server:
Conversationalist.find_by_sql(['
SELECT * FROM (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t1
LEFT JOIN (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t2
ON t1.chat_id = t2.chat_id',
recipient_id, recipient_type, sender_id, sender_type])
It works fine if there is a result. But if there is no result then I get an array with an empty Conversationalist object: [#<Conversationalist id: nil, conversable_type: nil...>]
Here is what I get as a result doing a direct query on the DB:
What I am expecting is an empty array since no rows should be returned but instead I get a result. How would I get an empty array if no results are returned?
ADDITIONAL CONTEXT
What I am trying to do is essentially a chat. When someone messages another user, the code above first checks to see if those two people are already chatting. If they are the message gets added to the chat. If not, a new Chat gets created and the message gets added:
class MessagesController < ApplicationController
def create
message = new_message
conversation = already_conversing?
if conversation.empty? || conversation.first.id.nil?
chat = Chat.new
chat.messages << message
chat.conversationalists << sender
chat.conversationalists << recipient
chat.save!
else
chat = Chat.find(conversation.first.chat_id)
chat.messages << message
end
head :ok
end
private
def new_message
Message.new(
sender_id: params[:sender_id],
sender_type: params[:sender_type],
recipient_id: params[:recipient_id],
recipient_type: params[:recipient_type],
message: params[:message]
)
end
def already_conversing?
Conversationalist.conversing?(
params[:recipient_id],
params[:recipient_type],
params[:sender_id],
params[:sender_type]
)
end
end
The Model:
class Conversationalist < ApplicationRecord
def self.conversing?(recipient_id, recipient_type, sender_id, sender_type)
Conversationalist.find_by_sql(['
SELECT * FROM (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t1
LEFT JOIN (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t2
ON t1.chat_id = t2.chat_id',
recipient_id, recipient_type, sender_id, sender_type])
end
end
So I was able to figure it out with the help of #Beartech from the comments above. Essentially the issue was happening because of the LEFT JOIN. If there are any results in t1 then Rails returns an array with an empty object. Similarly, if it was a RIGHT JOIN and t2 had a result, Rails would do the same. So the fix, in order to get an empty array, is to change the join to an INNER JOIN:
Conversationalist.find_by_sql(['
SELECT * FROM (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t1
INNER JOIN (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t2
ON t1.chat_id = t2.chat_id',
recipient_id, recipient_type, sender_id, sender_type])

How can i rewrite this sql using ActiveRecord?

I have a raw sql query here:
SELECT
min(users.updated_at)
FROM
users
WHERE
account_id = :account_id
AND state = 'completed'
AND amount > 0
and(
SELECT
sum(amount)
FROM
users t2
WHERE
t2.updated_at <= users.updated_at
AND t2.account_id = users.account_id
AND t2.state = 'completed'
AND t2.amount > 0) > 2000;
Intention of the query is to fetch the date when user has reached the amount of deposit equivalent to 2000.
I want to rewrite it using Rails's ActiveRecord helpers. My confusion starts with the subquery part. Although i could translate it so far is below:
User
.where(account_id: 5087)
.where.not(type: EXCLUDED)
.in_state(*DEPOSIT_STATES)
.deposits #amount > 0
.minimum('updated_at')
.select("and (select sum(amount) from users t2
where t2.updated_at <= users.updated_at
and t2.account_id = users.account_id
and t2.state = 'completed' and t2.amount > 0) > 2000")
Update
This query worked but this looks ugly. Any ideas to write it better:
User
.where(account_id: 5087)
.where.not(type: EXCLUDED)
.in_state(*DEPOSIT_STATES)
.deposits
.where("(select sum(amount) from users t2
where t2.updated_at <= users.updated_at
and t2.account_id = users.account_id
and t2.state = 'completed' and t2.amount > 0) > 2000")
.minimum('updated_at')

Order by not working in HQL inside Grails Controller

I have the following inside my controller
def pgtyp = Pgtyp.executeQuery("select p.date_hour,p.visits, p.mv, p.browser,p.pagetype,p.platform,p.device
from Pgtyp p
where p.pagetype = ? and p.device = ? and p.browser = ? and p.platform = ? order by p.date_hour[params.pagetype,params.device,params.browser,params.platform])
The moment I add 'order by p.date_hour', I get the following error:
java.lang.RuntimeException: FileWatcher caught serious error, see cause
Is there some other way I do an order by date_hour in HQL. How do I make this order by work?
This worked for me.!
order by col_0_0_ asc which is col_[fieldnumber]_0
with the first selected field being counted with 0 as fieldnumber

Rails: Search for person with language skills - e,g, speaks "German AND English" on one-to-many table

This must be a basic thing in rails, but I don't know how to do it.
I would like to filter participants based on the languages they speak. People can speak multiple languages, and languages are stored in their own table with a one-to-many relationship.
Now my search looks really clunky and doesn't seem to work:
if #cvsearch.language.present? == true and #cvsearch.language != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language)
else
#cvsearch.language = 0
end
if #cvsearch.language1.present? == true and #cvsearch.language1 != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language1)
end
if #cvsearch.language2.present? == true and #cvsearch.language2 != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language2)
end
if #cvsearch.language3.present? == true and #cvsearch.language3 != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language3)
end
The resulting SQL, slightly shortened:
SELECT COUNT(*) FROM "participants" INNER JOIN "languages" ON "languages"."participant_id" = "participants"."id" WHERE (participants.id >= 2) AND (languages.name = 11) AND (languages.name = 10)[0m
It would be great to get a specific solution, but even better is a pointer as to where I can read up on this - what's the key word I am missing to describe this problem?
So this is the solution I am using for now:
if #cvsearch.language1.present? == true and #cvsearch.language1 != 0
safe_lang = ActiveRecord::Base::sanitize(#cvsearch.language1)
qry = "INNER JOIN languages l1 ON l1.participant_id = participants.id AND l1.name = " + safe_lang.to_s
#p = #p.joins(qry)
end
Works wonderfully, just need to get some feedback regarding the safety of this approach.
I'm not sure of a general reference to refer you to, but this is basic SQL stuff. Basically, the JOIN is performed first resulting in a number of rows and then the WHERE is applied, filtering the rows. The conceptual mistake here is thinking that the WHERE clause will somehow apply to the full set of matched languages, but it doesn't work that way, each row of the result is considered in isolation, therefore a clause like (languages.name = 11) AND (languages.name = 10) will never return anything, because languages.name only has a single value in each row. The query as constructed could only work for an OR clause, so you could say something like WHERE (languages.name = 11) OR (languages.name = 12).
In order to filter down the participants you need one join for each language, so you want something like this:
SELECT COUNT(*) FROM participants
INNER JOIN languages l1 ON l1.participant_id = participants.id AND (languages.name = 10)
INNER JOIN languages l2 ON l2.participant_id = participants.id AND (languages.name = 11)
WHERE participants.id >= 2
Offhand I'm not sure of the easiest way to do this in ActiveRecord, it's not a super common query. Your general structure should work, but with something like:
if #cvsearch.language1.present? == true and #cvsearch.language1 != 0
safe_language = ActiveRecord::Base.sanitize(#cvssearch.language1)
join_clause = "INNER JOIN languages l1 ON l1.participant_id = participants.id AND language.name = #{safe_language}"
#p = #p.joins(join_clause)
end

Resources