rails, postgres and jsonb. query doesn't find records - ruby-on-rails

I can't get my query to find relevant records.
here's my test record:
model: Submission
column name: contents (jsonb)
record is created by: Submission.create(contents: {ada: 'ada'})
here's the query: Submission.where("contents->>'ada'='ada'").first
results is nil
record is in database and (lets assume record id=1)
Submission.find(1).contents[:ada]=='ada'
results in true

Try following, ref this
Submission.where('contents #> ?', {ada: 'ada'}.to_json)

Ok, so I'm still not exactly sure what's going on but the problem was in the fact that I was assigning a default values of {} in my migration. when this default assignment is removed everything works as expected.

Related

Rails - Querying a JSON Field

In my Rails 5 app, I'm using the Ahoy gem which uses a jsonb field (called properties) that I don't know how to query.
A sample of the data in the properties field:
"{\"id\":\"11\"}"
What I'm trying to do is:
Find the records created in the last seven days (using the time field)
Group the records by the id in the properties field
Order the records based on which id had the most records
I tried this:
#foo = Ahoy::Event.where(name: "Viewed Product").where(time: 7.days.ago..Time.now).group("properties(id)").order('COUNT("properties(id)") DESC')
but I received the error, PG::UndefinedColumn: ERROR: column "properties(id)" doe not exist
I found this somewhat similar question in the issues for the gem, so I tried breaking the query into parts:
#foo = Ahoy::Event.where(name: "Viewed Product").where(time: 7.days.ago..Time.now)
#foo.group("properties REGEXP '[{,]\"id\"...).count
but then my IDE gives the error unterminated string meets the end of file.
Can someone please help me determine what I'm doing wrong?
If you are using PostgreSQL, I believe that you would have to refer to json columns like this:
.group("properties ->> 'id'")

Rails 4 update_all and set value from another field

I need to do some bulk updates in some models and set value of a field as value of another field.
Right now I can do that with raw sql like this:
ActiveRecord::Base.connection.execute("UPDATE `deleted_contents` SET `deleted_contents`.`original_id` = `deleted_contents`.`id` WHERE `deleted_contents`.`original_id` is NULL")
This is working fine, however I need to do this using ActiveRecord query interface due to many reasons.
I tried:
DeletedContent.where(original_id: nil).update_all(original_id: value_of_id_column)
For value_of_id_column I tried :id, self.id, id, etc, nothing works. What should I set for value_of_id_column to get the original query generated by rails? Is this possible, or using the raw sql is the only solution?
Also I do not want to iterate over each record and update. This is not a valid solution for me:
DeletedContent.where(original_id: nil).each do |deleted_content|
update_each_record
end
I'm pretty sure you cannot obtain that query by passing a hash to update_all.
The closest to what you want to obtain would be:
DeletedContent.where(original_id: nil).update_all("original_id = id")

active record how do I specify a column after a join?

I'm trying to make an sql query using activerecord and I'm having a hard time specifying a specific column from multiple joined tables.
for instance in sql
select go.id, sequence.name, sequence.id from sequence join (goterms,...) on ...
this is not beautiful sql but my point is that I'm able to specify which .id I want returned
in activerecord I'm doing this:
results = Sequence.joins(:Foreigndb,:Goterm,:Taxa)
.select(:header,:taxaclass, :genus, :interpro_desc,:description,:dbname,:read_depth, :name)
.distinct
I want to be able to get id from :Goterm but :Taxa and :Foreigndb also use id as a column in the database so i'm getting uninformative errors that I assume stem from this issue when I do the following.
results = Sequence.joins(:Foreigndb,:Goterm,:Taxa)
.select(:header,:taxaclass, :genus, :interpro_desc,:description,:dbname,:read_depth, :name,:id)
.distinct
What is the correct way to just specify that I want Goterm.id?
edit - Here is the error:
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'Goterm.id' in 'field list'
when I run:
results = Sequence.joins(:Foreigndb,:Goterm,:Taxa).select(:header,:taxaclass,:genus, :interpro_desc,:description,:dbname, :read_depth, :name,'Goterm.id').limit(5).offset(0).dresults = Sequence.joins(:Foreigndb,:Goterm,:Taxa).select(:header,:taxaclass, :genus, :interpro_desc,:description,:dbname, :read_depth, :name,'Goterm.id').limit(5).offset(0).distinct
results = Sequence.joins(:Foreigndb,:Goterm,:Taxa).select(:header,:taxaclass, :genus, :interpro_desc,:description,:dbname,:read_depth, :name, 'sequences.id')
.distinct
It turns out that ilan's answer is correct, however be sure that everything is lower case. I was using 'Goterm.id' to make the selection when it needs to be 'goterm.id'
If anyone else runs into this, I also ran into difficulties grabbing the goterm.id data out of the returned query objects. Each time I called object.id on that return set it would give me something different from what I was expecting. I think the attribute I was expecting was being obscured by something else. To get the data I needed I did the following:
results = Sequence.joins(:Foreigndb,:Goterm,:Taxa).select(:header,:taxaclass, :genus,:interpro_desc,:description,:dbname,:read_depth, :name).distinct
firstRes = results[0]
firstRes.attributes['id']

Seeds file - adding id column to existing data

My seeds file populated the countries table with a list of countries. But now it needs to be changed to hard-code the id (instead of rails generating the id column for me).
I added the id column and values as per below:
zmb: {id: 103,code: 'ZMB', name: Country.human_attribute_name(:zambia, default: 'Error!'), display_order: nil, create_user: user, update_user: user, eff_date: Time.now, exp_date: default_exp_date},
skn: {id: 104,code: 'SKN', name: Country.human_attribute_name(:st_kitts_and_nevis, default: 'Error!'), display_order: nil, create_user: user, update_user: user, eff_date: Time.now, exp_date: default_exp_date}
countries.each { |key, value| countries_for_later[key] = Country.find_or_initialize_by(id: value[:id]); countries_for_later[key].assign_attributes(value); countries_for_later[key].save!; }
Above it just a snippet. I have added an id: for every country.
But when I run db:seed I get the following error:
ActiveRecord::RecordInvalid: Validation failed: Code has already been taken
I am new to rails so I'm not sure what is causing this - is it because the ID column already exists in the database?
What I think is happening is you have existing data in your database ... let's say
[{id:1 , code: 'ABC'},
{id:2 , code: 'DEF'}]
Now you run your seed file which has {id: 3, 'DEF'} for example.
Because you are using find_or_initialize_by with id you are running into errors. Since you can potentially insert duplicates.
I recon you should just clear your data, but you can try doing find_or_initialize_by using code instead of id. That way you wont ever have a problem of trying to create a duplicate country code.
Country.find_or_initialize_by(code: value[:code])
I think you might run into problems with your ids, but you will have to test that. It's generally bad practice to do what you are doing. Whether they ids change or now should be irrelevant. Your seed file should reference the objects that are being created not ids.
Also make sure you aren't using any default_scopes ... this would affect how find_or_initialize_by works.
The error is about Code: Code has already been taken. You've a validation which says Code should be uniq. You can delete all Countries and load seeds again.
Run this in the rails console:
Country.delete_all
Then re-run the seed:
rake db:seed
Yes, it is due to duplicate entry. In that case run ModelName.delete_all in your rails console and then run rake db:seed again being in the current project directory. Hope this works.
ActiveRecord::RecordInvalid: Validation failed: Code has already been taken
is the default error message for the uniqueness validator for :code.
Running rake db:reset will definitely clear and reseed your database. Not sure about the hardcoded ids though.
Check this : Overriding id on create in ActiveRecord
you will have to disable protection with
save(false)
or
Country.create(attributes_for_country, without_protection: true)
I haven't tested this though, be careful with your validators.
Add the line for
countries_for_later[key].id = value[:id]
the problem is that you can't set :id => value[:id] to Country.new because id is a special attribute, and is automatically protected from mass-assignment
so it will be:
countries.each { |key, value|
countries_for_later[key] = Country.find_or_initialize_by(id: value[:id])
countries_for_later[key].assign_attributes(value)
countries_for_later[key].id = value[:id] if countries_for_later[key].new_record?
countries_for_later[key].save(false)
}
The ids data that you are using in your seeds file: does that have any meaning outside of Rails? Eg
zmb: {id: 103,code: 'ZMB',
is this some external data for Zambia, where 103 is it's ID in some internationally recognised table of country codes? (in my countries database, Zambia's "numcode" value is 894). If it is, then you should rename it to something else, and let Rails decide what the id field should be.
Generally, mucking about with the value of ID in rails is going to be a pain in the ass for you. I'd recommend not doing it. If you need to do tests on data, then use some other unique field (like 'code') to test whether associations etc have been set up, or whatever you want to do, and let Rails worry about what value to use for ID.

proper way to debug sql result in rails

I'm currently using
puts #object.inspect
to debug the results of queries in rails, but the output doesn't seem to include any joins I've defined for the query. How do I get the full array to show?
For example, if I do the following
#object = Object.joins("JOIN associations ON associations.id = object.association_id")
.where(:id => params[:object_id])
.select("objects.*, associations.*")
.first
puts #object.inspect
I get the all the Object fields in my debug array, but none of the association fields. Yet they are there when I try to use them in my view (#object.association_field etc)
PS - the above query looks ugly, I'm only trying to pull one record, but I was getting various errors if I tried to use .find() instead of .where().first. Suggestions welcome on how to make it more railsy
Why not the simplest possible way:
#object = Object.find(params[:object_id])
#association = #object.associations.first
puts "#{#object.inspect}, #{#association.inspect}"
(assuming that has_many :associations is defined in Object)
And the reason you are not getting fields for association is because the only thing that joins does is joining to another table in SQL. It does not fetch joined data. select only select subset of object's attributes, I think the rest is just ignored.

Resources