Unexpected behaviour of array.count in ruby - ruby-on-rails

Hi i am using Rails3 when i puts array of subscribers like this
#subscribers = User.all
and puts it.gives me this array
[#<User id: 62, is_activated: true, subscriber: "TEST_DB2", ports_order: 100, created_at: "2012-05-21 14:47:48">, #<User id: 66, is_activated: true, subscriber: "JOHI", ports_order: 100, created_at: "2012-05-22 12:06:19">, #<User id: 68, is_activated: true, subscriber: "ALI-NAQWI", ports_order: 100, created_at: "2012-05-24 11:01:22">]
And when i give command
#subscribers.count #it give me 0 count
Why?????????

Try length instead, it can be a bug in AREL :)
That he is trying to lazy do something. He is giving back proxy object so it can be a problem there. Length will always work on your result value ~ array.

Related

How to use the PostgreSQL JSON array query ActiveRecord::Relation in Ruby on Rails

I want to query ActiveRecord::Relation with 51 fields.
the data structure is like this:
matings: {"ids"=>[50, 51, 64]}
or
matings: {"ids"=>51}
If I do:
MouseColony.where("CAST(matings AS TEXT) LIKE ?", "%51%")
=>#<ActiveRecord::Relation [
#<MouseColony id: 604, animal_no: "a0008", animal_desc: "", gender: "M♂", source: "外购", animal_status: "配对", cage_id: nil, generation: 0, birth_date: "2018-12-25", weaning_date: "2019-01-15", disable_date: nil, received_date: "2019-03-20", created_at: "2019-06-03 02:45:03", updated_at: "2019-06-03 03:14:37", user_id: 1, strain_id: 1, mating_id: 64, litter_id: nil, purchase_mouse_number: "17203", age: 223, mating_quantity: 3, matings: {"ids"=>[50, 51, 64]}, genotype_id: 10, experiment_id: nil, experiment_date: nil, age_weeks: 32>,
#<MouseColony id: 624, animal_no: "a0028", animal_desc: "", gender: "F♀", source: "外购", animal_status: "配对", cage_id: nil, generation: 0, birth_date: "2018-12-25", weaning_date: "2019-01-15", disable_date: nil, received_date: "2019-03-20", created_at: "2019-06-03 02:50:07", updated_at: "2019-06-03 03:09:11", user_id: 1, strain_id: 1, mating_id: 51, litter_id: nil, purchase_mouse_number: "17138", age: 223, mating_quantity: 5, matings: {"ids"=>51}, genotype_id: 9, experiment_id: nil, experiment_date: nil, age_weeks: 32>
]>
I tried to use MouseColony.where("CAST(matings ->> 'ids' AS TEXT) LIKE ?", "%51%") or using MouseColony.where("matings ->> 'ids' = ?", "51"), but the result is such a MouseColony Load (1.0ms) SELECT "mouse_colonies".* FROM "mouse_colonies" WHERE (CAST(matings ->> 'ids' AS TEXT) LIKE '%51%') LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
I've also tried to use this:
MouseColony.where("matings #>> '{ids}' = ?", "51")
But still can't find any data.
I think my problem might be here:
My models: mouse_colony.rb
store :matings, :accessors => [:ids], coder: JSON
and I storage record like this:
#mouse_colony.matings[:ids] = [50, 51, 64]`` #mouse_colony.save
You can try using the "contains" operator #>:
MouseColony.where("matings->'ids' #> '?'", 51)
You should be able to use the LIKE operator like this:
MouseColony.where("matings::json->>'ids' LIKE ?", "%51%")

Convert string to number in where clause

I have the following where clause:
OnlineCourseRegistration.where(course_class_id: 6, status: "Completed")
=> #<OnlineCourseRegistration id: 142, cart_id: 150, user_id: 7069, course_class_id: 681, created_at: "2017-07-15 22:06:06", updated_at: "2017-07-20 23:59:01", exam_attempts: 1, exam_completed_at: "2017-07-20 23:57:32", evaluation_completed_at: "2017-07-20 23:59:01", status: "Completed", score: "87", add_extension: false, retest_cart_id: nil, retest_purchased_at: nil>
I would also like to add that the score would be greater than 70, so something like...
OnlineCourseRegistration.where(course_class_id: 681, status: "Completed", :score.to_i > 70).last
...but of course, this doesn't work. Is there a way to do this within the where clause?
ActiveRecord where accepts string as query constructor
OnlineCourseRegistration.where("course_class_id = ? AND status = ? AND score > ?", 681, "Completed", 70).last
If you prefer not to use raw SQL you can use the Arel methods, for example:
OnlineCourseRegistration.where(course_class_id: 681, status: "Completed").where(OnlineCourseRegistration.arel_table[:score].gt(70)).last
For posterity I wanted to add these cast solutions as an answer also:
where("score::int >= ?", 80) # PostgreSQL-specific casting syntax
where("cast(score as int) >= ?", 80) # Standard SQL type cast

Rails how to calculate multiple sums from one query data

With the following two models, Company and Response, I am making a query of the total responses per company like this:
#allResponses = Company.find(current_user_company_id).responses
this gives me data like this:
[#<Response id: 1, company_id: 1, created_at: "2013-04-24 02:36:54", feedback_score: 10, feedback_explanation: "I really like the way you guys do xyz.", additional_data: "", updated_at: "2013-04-24 02:36:54">, #<Response id: 2, company_id: 1, created_at: "2013-04-25 03:51:07", feedback_score: 5, feedback_explanation: "customer service is spotty.", additional_data: "", updated_at: "2013-04-25 03:51:07">, #<Response id: 3, company_id: 1, created_at: "2013-04-25 03:52:04", feedback_score: 7, feedback_explanation: "You've got potential.", additional_data: "", updated_at: "2013-04-25 03:52:04">, #<Response id: 4, company_id: 1, created_at: "2013-04-25 03:52:18", feedback_score: 9, feedback_explanation: "Almost perfect.", additional_data: "", updated_at: "2013-04-25 03:52:18">]
I want to get the following two variables out of this data:
#sumOfHighScores = some.thing.here #sum of feedback_scores that are greater than 8
#sumOfLowScores = some.thing.here.too #sum of feedback_scores that are less than 7
You can try this,
#sumOfHighScores = #allResponses.select{ |response| response.feedback_score > 8 }.map(&:feedback_score).sum
#sumOfLowScores = #allResponses.select{ |response| response.feedback_score < 7 }.map(&:feedback_score).sum
Try this..
#sumOfHighScores = #allResponses.select{ |response| response.feedback_score > 8 }.sum
#sumOfLowScores = #allResponses.select{ |response| response.feedback_score < 7 }.sum
I will perform the entire calculation in the database.
company = Company.find(current_user_company_id)
totals = company.responses.sum(
:feedback_score,
:group => "CASE WHEN feedback_score < 7 THEN 'low' ELSE 'high' END")
low, high = (totals['low'] || 0), (totals['high'] || 0 )

Rails 3: use contents of an array as variables in a where method

I have three models: Isbn, Sale and Channel.
Aim: to get a list in the isbns show.html.erb view which looks something like this:
Isbn: myisbn
Total sales for myisbn: 100
Myisbn sales for channel 1: 50
Myisbn sales for channel 2: 25
Myisbn sales for channel 3: 25
Here are my models.
Isbn.rb model
has_many :sales
has_many :channels, :through => :sales
Sale.rb model (has attributes sales_channel_id, isbn_id, quantity)
has_many :channels
belongs_to :isbn
Channel.rb model:
belongs_to :sale
I've been working in the isbns controller, in the show method, just to get something to work. I thought I'd refactor later - advice on whether any of this stuff should go in the model would be most welcome.
So far I've got this:
#channelisbn = Sale.where("sales_channel_id =?',1).where("isbn_id=?",3)
#channelsalesisbn = 0
#channelisbn.each {|y| #channelsalesisbn =+ y.quantity}
This successfully gets all the sales where Channel ID is 1 and ISBN id is 3. But it's not much use, as the IDs are hard coded. So I got the Channel IDs into an array:
#channellist = Channel.all
#channel = 0
#channelarray = #channellist.map {|z| #channel = z.id}
which gives me a lovely array of [1,2,3,4]
But I can't figure out how to pass the 1, then the 2, then the 3 and then the 4 into a block which can be used to look up an ISBN's sales which have that sales channel id. This is what I tried (still hardcoding the ISBN id - thought I'd tackle one problem at a time), which returned an empty array:
#channelarray.each do |channel|
#channelisbn = []
#channelisbn = Sale.where("sales_channel_id = ?", channel).where("isbn_id = ?",3)
#channelsalesisbn = 0
#result = []
#result << #channelisbn.each {|a| #channelsalesisbn =+ a.quantity}
end
I was then going to sum the contents of the array.
Any help would be gratefully received. This is my first post, so my zero acceptance rate will change soon!
UPDATE
Just to finish this question off, here's where I've ended up, which is great, and ready for tinkering with: an array, nicely grouped, giving me sales by isbn by channel. Thanks for the group_by tip off!
#in the show action in the isbns controller:
#isbn = Isbn.find(params[:id])
#channelarray = Channel.select(:id).all
#channelarray.group_by {|i| Sale.where("channel_id = ?",i).where("isbn_id =?", #isbn)}
From the console, line breaks added for clarity:
(sneakily set #isbn = 3 first of all, since in the console you can't pass params from a view, so the #isbn instance defined in the controller is nil in the console)
ruby-1.9.2-p180 :067 > #channelarray.group_by {|i| Sale.where("channel_id = ?",i).where("isbn_id =?", #isbn)}
=> {[#<Sale id: 1, isbn_id: 3, quantity: 10000, value: 12000, currency: "GBP", total_quantity: nil, created_at: "2011-05-06 12:30:35", updated_at: "2011-05-07 17:43:13", customer: "Waterstone's", retail_price: nil, discount: nil, invoice_date: "2011-05-24">, #<Sale id: 2, isbn_id: 3, quantity: 1000, value: 500, currency: "GBP", total_quantity: nil, created_at: "2011-05-07 09:37:53", updated_at: "2011-05-07 19:14:52", customer: "Borders", retail_price: nil, discount: nil, invoice_date: "2011-02-05">]=>[#<Channel id: 1>],
[#<Sale id: 3, isbn_id: 3, quantity: 500, value: 1500, currency: "", total_quantity: nil, created_at: "2011-05-07 09:38:11", updated_at: "2011-05-07 19:15:07", customer: "Borders", retail_price: nil, discount: nil, invoice_date: "2011-12-05">, #<Sale id: 4, isbn_id: 3, quantity: 45, value: 300, currency: "", total_quantity: nil, created_at: "2011-05-07 09:38:38", updated_at: "2011-05-07 19:15:36", customer: "Borders", retail_price: nil, discount: nil, invoice_date: "2011-06-05">]=>[#<Channel id: 2>],
[]=>[#<Channel id: 3>],
[]=>[#<Channel id: 4>]}
UPDATE 2
Ha, the hash I generated had the key value pairs the wrong way round. The array containing the sales data was the key - it should have been the value. Rubydocs saved the day:
#salesbychannel = #salesbychannelwrong.invert
The invert method switches the key-value pairs. Sweet.
What you're looking for is passing an array to a ARel#where(), like this:
Sale.where(:sales_channel_id => #channelarray)
This should execute an IN query. If that's not working, you can always pass the array to ActiveRecord#find, like this:
Sale.find(#channelarray)
Hope this helps

Finding elements in array of hashes

I'm trying to build a model of products which has many components. Some components are optional and depend on the choice the user is making to enable them or not.
I have two models, one is configuration and the other is elements (of that configuration).
At the beginning I bring all the elements of the array, and then create another array of those which will be shown by default.
But when I write the following code it gives me an error despite both objects being arrays of hashes.
So I bring my first array of all elements:
irb(main):252:0* #all = Configuration.find(1).elements
=> [#<Element id: 1, name: "elem1", quantity: 1, position: 1, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 2, name: "elem2", quantity: 2, position: 2, subposition: 1, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 3, name: "elem3", quantity: 3, position: 2, subposition: 2, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 4, name: "elem4", quantity: 4, position: 3, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>]
Then I filter to be only those that have a subposition nil or 1
irb(main):253:0> #default = #all.where(:subposition=>nil).concat(#all.where(:subposition=>1))
=> [#<Element id: 1, name: "elem1", quantity: 1, position: 1, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 4, name: "elem4", quantity: 4, position: 3, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 2, name: "elem2", quantity: 2, position: 2, subposition: 1, created_at: nil, updated_at: nil, configuration_id: 1>]
So far so good, as you can see, Elem3 is not being shown in #default as it doesn't meet the requiements.
The problem comes when I try to play with the arrays as I need to perform certain operations.
irb(main):257:0> #all.where(:position =>1)
=> [#<Element id: 1, name: "elem1", quantity: 1, position: 1, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>]
But the same operation in #default will fail,
irb(main):258:0> #default.where(:position =>1)
NoMethodError: undefined method `where' for #<Array:0x2641660>
Now, they're both arrays of hashes and look the same, why is the same method failing in the second case?
Thanks a lot for your help!
Throughout your code, #all is an ActiveRecord::Relation, not an array. This lets you perform the standard .where call (among others). When you assigned to #default, you used .concat which evaluated the query and assigned an actual array to #default.
You might try a different approach in your second code block. Maybe something like this:
#default = #all.where("subposition is null or subposition = ?", 1)
Well, your problem is that concat transforms a collection into an array.
I'd replace:
irb(main):253:0> #default = #all.where(:subposition=>nil).concat(#all.where(:subposition=>1))
by:
#default = #all.where("subposition = '1' OR subposition = nil") #I'm unsure of the nil in the statement, I nerver remember, try NULL if it fails
This way, you make only one db query and you keep an ActiveRecord collection.
Thus, you'll be able to chain other where conditions on it.

Resources