In a Rails 5.2 app, the User model has the subscriptions jsonb attribute
t.jsonb "subscriptions", default: [], null: false
An example is
irb(main):091:0> User.first.subscriptions
=> [{"id"=>"dfdaad60", "type"=>"Organization"},
{"id"=>"18616f4d", "type"=>"Project"}]
How can I, using arel, get the ids of all the subscriptions having type equal Project?
I have tried to use the #> operator without success.
Following will return array of ids for subscriptions(having type 'Project') of Users,
User.pluck(:subscriptions).flatten.map { |x| x['id'] if x['type'] == 'Project' }.compact
You do not need where AR or AREL query as you need only ids present inside subscriptions for type having 'Project' & can be get as above.
This will do the trick, using a custom SQL statement:
sql = "SELECT value->'id' AS id FROM users, jsonb_array_elements(users.subscriptions) WHERE data #> '#{[{type: "Project"}].to_json}'"
result = ActiveRecord::Base.connection.execute(sql)
puts result.to_a
You can use the ActiveRecordExtended gem to find all users which have a project type subscription:
User.where.contains(subscriptions: { type: "Project" })
I would then get ids in ruby.
Related
I've a hstore field in a database table. I want to write a Query to find records matching with any array element in any hash of hstore field using ruby on rails.
Users Table
--------------------------------------------------------------------------------
ID Name sectors(hstore)
1 Piotr {"aviation"=>"0", "oil_and_gas" => "50", "transport" => "50"}
2 reza {"oil_and_gas" => "70", "energy" => "30"}
3 pat {"transport" => "40", "energy" => "60"}
4 Kim {"infrastructure" => "20", "healthcare" => "20", "industrial" => "60"}
considering above test data, I want to write a query on hstore field to get all records having any key like ['oil_and_gas', 'energy', 'transport']
I can match and find single sector records as its mentioned in https://nandovieira.com/using-postgresql-and-hstore-with-rails, but my requirement is to find any record where hstore hash is having any one key matching with any one element of array.
I'm using Rails 5.1.6.2, ruby 2.5.3
May be you are looking for the following operator:
hstore ? key # does hstore contain key?
Query may looks like so:
User.where("sectors ? 'oil_and_gas'")
.or("sectors ? 'energy'")
.or("sectors ? 'transport'")
According to postgresql docs
Check for a specific key in hstore column You can check for a specific
key in an hstore column using the ? operator in the WHERE clause. For
example, the following statement returns all rows with attr contains
key publisher.
SELECT
title,
attr->'publisher' as publisher,
attr
FROM
books
WHERE
attr ? 'publisher';
Hi I have a controller function, based on a check_box_form ( so there can by one or more value passed), like this:
def show_spells
#there are 3 params 'elemensts', 'levels', and 'tags'
if params[:tags].blank?
#chosen_spells = Spell.where(element: params[:elements], level: params[:levels] ).to_a
else
#chosen_spells = Spell.where( "element = ? and level = ? and tags = ARRAY[?]", params[:elements], params[:levels], params[:tags]).to_a
end
end
And it references to a object table:
class CreateSpells < ActiveRecord::Migration
def change
create_table :spells do |t|
t.string :element
t.string :level
t.string :desc
t.text :tags, array: true, default: []
t.timestamps null: false
end
end
end
The firs query works perfect, but the second one is making a problem, when I pass just one value to elements and levels it doesn't show any resoults, and when I pass more then one walue to elements or levels it show me an error: PG::InvalidTextRepresentation: ERROR: invalid input syntax for type boolean
I`m new to SQL query searches, but from what I understand, from Active Record Query Guide :
Object.where(attr: params[:foo]) should be equal to Object.where("attr = ?", params[:foo]). And it seems like it isn`t. Any help would be appreciated. :)
There are a few different issues here that you're running into. However, I believe all of them are related to bad query syntax. ActiveRecord's query interface is very powerful, but without proper usage, it can throw crazy errors that are hard to debug. A good understanding of SQL will help you navigate it better.
First, regarding "pass[ing] more then one value to elements or levels": the = operator in Postgres can only compare against a single value, and Rails' parameter binding is very simplistic. When you run this code:
Object.where("attr = ?", [1, 2, 3])
Rails is going to run a query like:
SELECT * FROM objects WHERE attr = 1, 2, 3;
Which is invalid SQL -- but Rails doesn't know that, so you get a PGError. The proper SQL syntax is:
SELECT * FROM objects WHERE attr IN (1, 2, 3);
And to get that, the best way is to use where's hash syntax:
Object.where(attr: [1, 2, 3])
Second, for an array type, the operation you're looking for to test inclusion, but Rails only knows to test for equality. You have no choice but to just use interpolated params for this, like you're doing, but your syntax is off. The SQL needs to be something like:
SELECT * FROM objects WHERE some_array_field #> ARRAY['foo', 'bar'];
To get that, .where("some_array_field #> ARRAY[?]", things) is your best bet.
In conclusion, your code could be written like this:
#chosen_spells = Spell.where(element: params[:elements],
level: params[:levels])
.where('tags #> ARRAY[?]', params[:tags])
And it should work as desired.
I'm developing an marketplace app where sellers can list items to sell. On the listing show page, I developed a widget called "other items from this seller". I use the below query to pull these items into the view.
Listing.where(:user_id => #listing.user)
This works but it includes the activerecord. Since this is a widget for "other items" I want to exclude the listing whose page the user is currently on. Note that the "user_id" field in the listing model is the sellers user id.
How would I modify this? I tried the below but it gives me an error saying it expects an "=>".
Listing.where(:user_id => #listing.user, :id != #listing.id)
How about simply using array parameter to where:
#listing.user.listings.where(["id != ?", #listing])
OR (better performance)
Listing.where(["user_id = ? AND id != ?", #listing.user_id, #listing.id])
For completeness, if you are using rails 4.x you could use .where.not
Listing.where(user_id: #listing.user_id).where.not(id: #listing.id)
In addition to RAJ's answer, you can chain the two clauses to make one query
Listing.where(:user_id => #listing.user).where("id != ?", #listing.id)
using Rails 3.2, there is a way to find out if a column is a reference column to other model?
I don't want to rely in "_id" string search in the name.
thanks.
UPDATE:
I need to iterate over all columns and made a special treatment in references columns, something like:
result = Hash.new
self.attribute_names.each do |name|
if self[name]
result[name] = self[name]
if name is reference column
insert xxx_description (from the other model) in hash.
end
end
end
I will return this hash as json to client.
{name: 'joseph', sector_id: 2, sector_name: 'backend'...}
Where sector_name, is person.sector.name...
thanks.
alternative method if you don't know the name of the association :
Post.reflect_on_all_associations(:belongs_to).map &:foreign_key
# => ['author_id','category_id']
See http://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html
Post.reflections[:comments].primary_key_name # => "message_id"
How to get activerecord associations via reflection
I receive a list of UserIds(about 1000 at a time) sorted by 'Income'. I have User records in "my system's database" but the 'Income' column is not there. I want to retrieve the Users from "my system's database"
in the Sorted Order as received in the list. I tried doing the following using Active Record expecting that the records would be retrieved in the same order as in the Sorted List but it does not work.
//PSEUDO CODE
User.all(:conditions => {:id => [SORTED LIST]})
I found an answer to a similar question at the link below, but am not sure how to implement the suggested solution using Active Record.
ORDER BY the IN value list
Is there any other way to do it?
Please guide.
Shardul.
Your linked to answer provides exactly what you need, you just need to code it in Ruby in a flexible manner.
Something like this:
class User
def self.find_as_sorted(ids)
values = []
ids.each_with_index do |id, index|
values << "(#{id}, #{index + 1})"
end
relation = self.joins("JOIN (VALUES #{values.join(",")}) as x (id, ordering) ON #{table_name}.id = x.id")
relation = relation.order('x.ordering')
relation
end
end
In fact you could easily put that in a module and mixin it into any ActiveRecord classes that need it, since it uses table_name and self its not implemented with any specific class names.
MySQL users can do this via the FIELD function but Postgres lacks it. However this questions has work arounds: Simulating MySQL's ORDER BY FIELD() in Postgresql