Inconsistent results from active-record query - ruby-on-rails

This is my Person model which has the following query in a method:
def get_uniq_person_ids
uniq_person_ids = select('person_id').where(:state => '1').uniq
uniq_person_ids
end
My Test is as follows:
def test_uniqueness
Person.delete_all
assert_equal(0, Person.count)
# ..... Adding 8 rows to the database with 2 unique person_id.....
pids = Person.get_uniq_person_ids
assert_equal(pids.size, 2)
end
Test fails with the following:
Failure:
<8> expected but was
<2>.
There are 8 rows but only 2 unique person_id in the table.
This is what I tried:
puts pids before assert. It prints only 2 objects. Test fails with the above message.
binding.pry right before the query. Size is 2 which is expected and the test passes this time.
Why is the result so inconsistent? Is it a timing issue?
Note: I am using sqlite as my database.

Okay, so I am not sure what the actual issue was but the following solved it:
def get_uniq_person_ids
uniq_person_ids = select('person_id').where(:state => '1').uniq
uniq_person_ids = uniq_person_ids.map(&:person_id)
uniq_person_ids
end
I added the line uniq_person_ids = uniq_person_ids.map(&:person_id) to create the array of person_ids.

Related

Update record if exists with roo gem

I have an import working correctly from a Spreadsheet using Roo gem.
The problem is every time I call the rake task, new records are created.
I want to update_attributes of the records in case the record exists.
Is there any way to approach this? I've tried this with no luck:
namespace :import do
desc "Import data from spreadsheet" # update this line
task data: :environment do
data = Roo::Spreadsheet.open('lib/t3.xlsx') # open spreadsheet
headers = data.row(1) # get header row
data.each_with_index do |row, idx|
next if idx == 0 # skip header
# create hash from headers and cells
product_data = Hash[[headers, row].transpose]
product = Product.new(product_data)
puts "Guardando Producto #{product.name}"
if product?
product.update_attributes
else
product.save!
end
rescue ActiveRecord::RecordInvalid => invalid
puts invalid.record.errors
end
end
end
if product? will never return false. You're testing whether the variable contains a falsy value (nil/false) or any other value. After calling product = Product.new, the value stored in product can never be nil or false.
What you want is to first find, and if not found, new, and then update_attributes on the resulting object:
product = Product.find_by(product_data.name) || Product.new
product.update_attributes(product_data)

Applying filters to Rails model

def show_category
category_selected = params[:genre]
all_movies = Movie.all
#movies_in_category = all_movies.where(:category => category_selected)
puts "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
puts category_selected
puts #movies_by_category.length
end
I have the above controller function which gets called with a parameter.
params[:genre]
the above line print out the parameter just fine.
all_movies = Movie.all
#movies_in_category = all_movies.where(:category => category_selected)
But the above 2 lines of code don't seem to be executed at all (I don't see the SQL queries for the above 2 lines being printed on the Rails console.
I also tried this but still couldn't see the SQL on the Rails console:
#movies_in_category = Movie.where(:category => category_selected)
What am I doing wrong here?

gem bulk_insert: undefined method `result_sets' for nil:NilClass?

I'm using gem bulk_insert for the first time: https://github.com/jamis/bulk_insert
I successfully used the gem to bulk copy records from one table to another. Later on, I needed to report the number of new records. I don't see anyway to get a row count back from bulk_insert, so I turned to return_primary_keys and result_sets to get a count, as shown in the Readme.
I added inserted = to line 3 and added the last line below:
columns = %i[first_name, last_name, email, referal]
inserted = User.bulk_insert(*columns, ignore: true, return_primary_keys: true) do |bulk|
bulk.set_size = BATCH_SIZE
registrants.select(:fname, :lname, :email).find_in_batches(batch_size: BATCH_SIZE) do |batch|
batch.each do |reg|
bulk.add [reg.fname, reg.lname, reg.email, 'self-registered']
end
end
end
puts "added #{inserted.result_sets.count} self-registered users"
Now, I get NoMethodError: undefined method 'result_sets' for nil:NilClass on the puts line.
I've read the Readme several times and searched for the problem with no results. Also checked that I've got the latest version - 1.7.0
What am I missing? How can I get to result_sets? Or better yet, can I get a record count without retrieving the entire list of new primary keys?
According to this issue on the repo, you need to create a bulk_insert worker first. So, in your case I think it would look something like this:
columns = %i[first_name, last_name, email, referal]
insert_worker = User.bulk_insert(*columns, ignore: true, return_primary_keys: true)
insert_worker.set_size = BATCH_SIZE
registrants.select(:fname, :lname, :email).find_in_batches(batch_size: BATCH_SIZE) do |batch|
batch.each do |reg|
insert_worker.add [reg.fname, reg.lname, reg.email, 'self-registered']
end
end
puts "added #{insert_worker.result_sets.count} self-registered users"
I found the answer in https://github.com/jamis/bulk_insert/issues/35, with credit to butnaruandrei. I had to create the bulk_insert object first, then call .add() later...as opposed to passing a block to bulk_insert with .add() within.
IMPORTANT: I also had to add inserter.save! after all add's. Without this, the last half-batch never gets saved...where it did get saved when passing a block in. This was not obvious at first, as everything seemed to be working...but the last few records were not saved without it.
This code works correctly:
columns = %i[first_name, last_name, email, referal]
inserter = User.bulk_insert(*columns, ignore: true, return_primary_keys: true)
inserter.set_size = BATCH_SIZE
registrants.select(:fname, :lname, :email).find_in_batches(batch_size: BATCH_SIZE) do |batch|
batch.each do |reg|
inserter.add [reg.fname, reg.lname, reg.email, 'self-registered']
end
end
inserter.save!
count = inserter.result_sets.map(&:rows).flatten.count
puts "added #{count} self-registered users"

Rails benchmark reports less time than activerecord query

I'm trying to benchmark this query:
Person.where(age: 60)
When I run it in the console it says:
Person Load (1.2ms) SELECT "people".* FROM "people" WHERE "people"."age" = ? [["age", 60]]
When I benchmark it, it reports 0.17ms
def self.get_ages_sql
sixties = Person.where(age: 60)
end
Benchmark.bmbm do |x|
x.report('sql') {Person.get_ages_sql}
end
Whats the discrepency between:
0.17ms (benchmark)
vs
1.2ms (reported when I run command in console)
This code does not do database request actually:
Person.where(age: 60)
It just build ActiveRecord::Relation.
You can ensure by executing next code line by line in console and watch wich line actually produce DB request:
relation = Person.where(age: 60); 1
relation.class.name
relation.to_a
But Console misleads you with "Person Load ..." because it calls additional method like #inspect on each code line result. And this additional method causes DB request:
relation = Person.where(age: 60).inspect; 1
And that is why your benchmark is wrong - you do test of query creation not whole DB request. It should look like:
def self.get_ages_sql
Person.where(age: 60).to_a
end
Added: To get deep into Console, create
class ConsoleTest
def inspect
data.inspect
end
def data
'Doing DB request'
end
def self.test_data
ct = self.new
puts 'No request yet'
ct.data
end
end
and then try in Console:
ct = ConsoleTest.new
and
ct = ConsoleTest.new; 1
ct.data
and
ConsoleTest.test_data

Rails: Finding records that overlap

I have a job model that has_many :fonctions and in my job model I'm creating a method to give me the number of similar fonction in all job compared to a given job.
ex: I want to compare all my job to Job1 job1 has this fonction("strategy", "management", "marketing", entrepreneurship")
another job job2 has this fonction( "strategy", "management", "data science")
So this must give me when doing (job1 & job2).size 2
for this i have this method in my job model that must do a similar job but the problem that i get this error undefined local variable or method for job'
def fonctions_score
(job.fonctions.collect(&:id) & self.fonctions.collect(&:id)).size
end
Update
This is the code that I'm trying now but still getting this error wrong number of arguments (0 for 1)
def fonctions_score(other_job)
these = other_job.fonctions.collect {|f| f.id }
those = self.fonctions.collect {|f| f.id }
logger.debug these logger.debug those # should just have lists of ids
common = (these & those)
logger.debug common # should be common ids
common.size
end
in my controller I'm ordering jobs like this
#related_jobs = Job.all
#related_jobs.sort_by do |related_job|
related_job.final_score
end
Try this
def calculate(j1, j2)
(j1.fonctions.pluck(:id) & j2.fonctions.pluck(:id)).size
end
Or
def calculate
self.fonctions.count
end

Resources