Order by number in Postgres JSONB column - ruby-on-rails

I have a position key defined in my JSONB column.
The values are treated as text, so the following query
MyModel.order("data ->> 'position' ASC").each {|x| puts x.position}
returns:
0
1
10
2
3
How can I treat position as integer and order my model based on that?

Purely a guess, but maybe:
MyModel.order("(data ->> 'position')::Integer ASC").each {|x| puts x.position}

Related

how to improve query using rails and postgres

I have three models postshas one for post_rating, post_waiting_time
post.rb
name_column type example
id integer 20
name string 'welcome'
user_id integer 1
post_rating.rb
name_column type example
id integer 10
rating_label enum poor,average,great
post_id integer 20
post_waiting_time.rb
name_column type example
id integer 10
waiting_label enum 0-3,4-6,7-10
post_id integer 20
i try use rating_label, waiting_label in query use eager_load, joins but can't write multi label in query
posts_lists = user.posts.eager_load(:post_rating, :post_waiting_time).where("post_waiting_times.waiting_label = ? ", '0-3')
this query work good with one value but i need multi query use waiting_label for example 0-3, 4-7 i try use IN() but i have error
Post.eager_load(:post_rating, :post_waiting_time).where('post_waiting_times.waiting_label IN( ? )', 'more_30,0-
3')
error message for last query
ActiveRecord::StatementInvalid (PG::InvalidTextRepresentation: ERROR: invalid input value for enum tag_label: "more_30,0-3")
LINE 1: ...ERE (post_waiting_times.waiting_label IN( 'more_30,0...
i fix this with
write query for filter by rating_label, waiting_label
posts_lists = user.posts
posts_lists.each do |record|
flag_filter = false
unless waiting_filter.empty?
flag_filter = if waiting_filter.include?(record.post_waiting_time.waiting_label)
true
else
false
end
end
unless rating_filter.empty?
flag_filter = if rating_filter.include?(record.post_rating.rating_label)
true
else
false
end
end
if flag
puts record
end
but this not efficiency way
i use Rails 6, PostgreSQL
You are doing it wrong, you can directly pass an array for comparison in rails. Here is the updated query
Post.eager_load(:post_rating, :post_waiting_time).where(post_waiting_times: {waiting_label: ['more_30','0-3']})
Hope this solves the problem.

Rails/Postgres Concat JSONB keys together in select

How can I obtain an aggregate of different keys within a JSONB column? Some of the data I want to show in the aggregated form would be a count & total (easy) but the other would be an array of a concatenation of two keys. I keep getting an error when trying to concat them within the inline sql.
context.request_groups
.joins(:request_items)
.select("request_groups.id as id,
count(*) as count,
array_agg(request_items.data->>'contact_first_name' || ' ' || request_items.data->>'contact_last_name') as names,
sum(cast(request_items.data->>'amount' as float)) as total")
.group(:id)
I'm currently trying to use the ARRAY_AGG function (example: http://www.postgresqltutorial.com/postgresql-aggregate-functions/postgresql-array_agg-function/) and getting the following error:
ActiveRecord::StatementInvalid (PG::UndefinedFunction: ERROR: operator does not exist: text ->> unknown)
LINE 1: ...t_name' || ' ' || request_items.data->>'contac...
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
I'm looking for results like:
id, count, total, ['first last', 'first last']
Heres the table layout:
----------------------
Request Group
----------------------
id | UUID
created_at | Timestamp
----------------------
----------------------
Request Items
----------------------
id | UUID
group_id | UUID
data | JSONB
----------------------
JSONB layout:
id: 3,
group_id: 2
data:
{
"name"=>"Fun Event",
"amount"=>200,
"contact_first_name"=>"Jeff",
"contact_last_name"=>"Person"
}
Following one should work I hope, there was problem in your code where constructing expression in array_agg method. Just adding open and close brackets will solve the issue.
Old one: request_items.data->>'contact_first_name'
Updated one: (request_items.data ->> 'contact_first_name')
If brackets not used, there would be problem in distinguish arguments of array_agg method.
context.request_groups
.joins(:request_items)
.select("request_groups.id as id,
count(*) as count,
array_agg( (request_items.data ->> 'contact_first_name') || ' ' || (request_items.data ->> 'contact_last_name') ) as names,
sum(cast(request_items.data->>'amount' as float)) as total")
.group(:id)

Rails query interface: selecting rows in database where any of the JSON array's values match a certain criteria

Database info:
Database: PostgresSQL
Table name: publishing_rules
Column name: menu_items
Column format: JSON
Example column value: {"items":[{"id":1,"title":"dfgdfg"},{"id":2,"title":"sdf"}]}
I need to gather all columns which have at least one item with an id equal to my value. So far I've come up with this:
id = 1
items = PublishingRule.where("menu_items #> '{items,0}' ->> 'id' = ?", id.to_s)
However this code only acquires columns with items array first value matching my criteria. I need to modify my code to something similar to:
items = PublishingRule.where("menu_items #> '{items, ANY}' ->> 'id' = ?", id.to_s)
or
id = 1
items = PublishingRule.where("menu_items #> '{items.map}' ->> 'id' = ?", id.to_s)
How do I do that?
Since the items is array at given example you can't work it out using only operators. You need to use jsonb_array_elements() in order to look into that.
Here's SQL query example to meet your requirement:
SELECT *
FROM publishing_rules
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements( menu_items -> 'items' )
WHERE value ->> 'id' = '2'
LIMIT 1
);
So, using within WHERE EXISTS lookup into the array does the trick.

Rails postgres hstore: query for a specific key with any of the given values

My question is specific to rails+postgres hstore datatype.
The WHERE IN [1,2, 3] or the rails equivalent Model.where(data: [1,2,3]) works fine for regular columns, but not for hstore.
I have a hstore column(say info) and I want to query for rows which have a particular key and any one of the given values.
For example: To find all books that have a key as 'author' and value as 'ABC' in hstore column, the following query works fine:
Book.where("info #> hstore(:key, :value)", key: "author", value: "ABC")
But I need a query that returns records which have a key as 'author' and any one of values in ['ABC', 'XYZ', 'PQRS', 'DFG'].
Any suggestions?
Maybe try:
Book.where("(info -> :key) IN (:values)", key: 'author', values: ['ABC', 'XYZ'])
However, #> has index support, while this won't use any indexes.

why does Rails think 6,500 is greater than 10,000?

I seeded a database with four different values in the "result" column. They're supposed to represent dollar values but that's irrelevant.
10,000; 6,500; 1,000; and 0
In the model, I created this class method
def self.result
order("result DESC")
end
In the controller, I called it
#decisions = Decision.result
In the index, it's listing them in the following order
6,500;
10,000;
1,000;
0
When I switch DESC to ASC....
def self.result
order("result ASC")
end
it reverses the order
0
1,000;
10,000;
6,500;
You're storing the numbers as strings. Change the column type to DECIMAL.
Your column type is string, not number, they are ordered by string order.
What is the data type of that column? If it's a string then a string starting with 6 is "bigger" than one starting with 1

Resources