I have a json column (is an array of objects) in my db containing the many categories related to a product like this:
[
{
"id": 1,
"name": "Category X"
},
{
"id": 2,
"name": "Category Y"
},
...
]
I need to translate into active record query the following PostgreSQL query:
SELECT * FROM products p, json_array_elements(p.category) as data
WHERE data ->>'name' = 'Sport';
Some of queries that I tried:
sports = Product.where("category ->>'name' = ?", "Sport")
Which returns an empty array (It is wrong since I have records with Sport category in my db)
sports = Product.where("category #> ?", [{name: "Sport"}])
Which raises: TypeError: can't quote Hash
sports = Product.where("category #> ?","{name: 'Sport'}")
Which raises: ERROR: operator does not exist: json #> unknown
and the Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
So I tried:
sports = Product.where("category #> ?", "[{'name': 'Sport'}]")
and
sports = Product.where("category #> ?", "[{'name': 'Sport'}]".to_json)
and some other queries all without success.
These links:
PostgreSQL functions-json
active record querying
didn't help much.
The reason why you're getting a PG::UndefinedFunction: exception ("operator does not exist: json #> unknown") is because the #> operator is meant to be used in jsonb data type columns, and your products.category column isn't.
For that you can; or to update the data type of the category column, or to explicitly cast the column at the moment of performing the query:
Product.where('category::jsonb #> ?', [{ name: 'Sport' }].to_json)
This:
Product.where("category #> ?", "[{'name': 'Sport'}]")
isn't going to work, since it's not valid syntax.
Related
I have a table with a jsonb column that has a nested json array. I would like to find all records where the nested array contains at least one of a value.
For instance, my model Person has a jsonb column named preferences. Inside the jsonb preferences, there are a few keys, one of which the value is an array:
Person.first.preferences = {"diet" => {"foods" => ["apples", "oranges", "bananas"]}}
I would like to create a query that returns all Persons whose preferences -> diet -> foods include 'apples' OR 'mangos' (for example).
Currently, I can get results with 1 input (ie. 'apples') like this:
Person.where('preferences #> ?', {'diet' => {'foods' => ['apples']}}.to_json)
And multiple inputs, if they all exist in the array. So if I pass in 'apples' and 'oranges', the result is returned because the record includes BOTH apples and oranges.
Person.where('preferences #> ?', {'diet' => {'foods' => ['apples', 'oranges']}}.to_json)
Is there a way to pass in multiple variables (ie. ['apples', 'mangos']) and check if the nested jsonb array contains either apples OR mangos but not necessarily both?
The following query has no results, because my Person's food preferences don't include mangos, even though they include apples.
Person.where('preferences #> ?', {'diet' => {'foods' => ['apples', 'mangos']}}.to_json)
Using Rails 5.2 and Postgresql 10
Thanks!
The first thing to do is to get to the array using -> or #> and then you can use the ?| operator to check if the array overlaps what you're looking for. Something like this SQL:
preferences->'diet'->'foods' ?| array['apples', 'mangos']
or with ActiveRecord:
Person.where("preferences->'diet'->'foods' ?| array[:foods]", foods: ['apples', 'mangos'])
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.
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.
I am trying to join two table columns with jsonb datatypes. Here are some models with jsonb:
User.create(emails: ['foo#bar.com', 'foo2#bar2.com'])
Email.create(
to: [
{email: 'foo#bar.com', name: 'foo bar'},
{email: 'foo3#bar3.com', name: 'foo bar3'}
],
from: 'foobar#bar.com'
)
I have tried:
joins("LEFT JOIN emails ON (users.emails ? emails.from_email OR emails.to->'email') ?| users.emails")
And I get: ActiveRecord::StatementInvalid: PG::DatatypeMismatch: ERROR: argument of OR must be type boolean, not type jsonb
I have also tried using the where method instead:
email_addresses = User.last.pluck(:emails).flatten
emails = Email.where(
"(to->'email' ?| :email_addresses OR from ?| :email_addresses)",
email_addresses: email_addresses
)
And I get ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near "to". I assume this is because the column is an array of objects.. not sure how to get past the array part.
I store an array of Section ids as integers. event.sections #=> ["1","115","130"]
There is no Events has_many Sections relationship. Maybe this is a problem. I only need id's from Section and nothing else, so I have the array of integers stored as a serialized string in Postgres.
I can do something like this, which returns an array of events:
Event.upcoming.select { |m| m.sections.include? #section.id.to_s}
Is there a way to query this to get back an ActiveRecord::Relation?
edit-----
My earlier select query is not correct, because if #section.id = "1" then it will match and select events with these id's "1", "10", "21", "100"
This is the proper select statement:
Event.upcoming.select {|e| ( e.newsletters.split(",").flatten.grep /^#{#section.id.to_s}$/ ).presence }