Order an Active Record Relation with a custom method - ruby-on-rails

I have to order an ActiveRecord Relation.
Profile.where(id: [1,2,3]).order_user_preferences
where records with country_code = 'US' (or a specific string) first.
I've created this self.order_user_preferences, but I can't access the relation object inside the method.
I think "sort_by" converts the ActiveRecord Relation to an array. Do you know how to keep it as an "Active record relation"?
class Profile
def self.order_user_preferences()
# Order by user country
countries = ['US']
# User-country first
return relation.sort_by {|p| countries.include?(p.country_code) ? 0 : 1} if countries.size > 0
end
end
Expected results
PROFILE ID | Country code
1 | US
2 | EU
3 | JP

You need order not sort_by. Sort by sort on the enumerable object and it is not aware of associations. Read more here: https://apidock.com/ruby/Enumerable/sort_by
So you will probably end up with something like Profile.joins(:countries).where(id: [1,2,3], countries: {country_code: "US"}).order("countries.country_code asc")
This will bring you any profiles where the id is 1 or 2 or 3 and they HAVE an associated country with country. Profiles that do not have an associated country will not be included.

Related

RAILS: How to select fields from associated table with grouping and create a hash from result?

I would like to create an active record query and store the result in a hash which includes summary information from associated tables as follows.
Here is the tables and associations:
Post belongs_to: category
Category has_many: posts
Here I would like to count the # of posts in each category and create a summary table as follows (with the SQL query for the desired table):
select c.name, count(p.id) from posts a left join categories c on p.category_id = c.id where p.status = 'Approved' group by (c.name) order by (c.name);
Category | count
---------------+-------
Basketball | 2
Football | 3
Hockey | 4
(3 rows)
Lastly I would like to store the result in a hash as follows:
summary_hash = { 'Basketball' => 2, 'Football' => 3, 'Hockey' => 4 }
I will appreciate if you can guide me how to write the active record query and store the result in the hash.
Try
Post.where(status: 'Approved').joins(:category).
select("categories.name").group("categories.name").count

Activerecord query with array in where clause

I have a location table in my rails app which has four columns :-
| Id | Longitude | Latitude | user_id
Now I have an array containing the list of user_ids. How can I write an active record query to select the last row of each user id. For example, if I have three user_ids in my array [1,2,3] I want the query to return just the last row corresponding to each user_id (lets assume entries for each user_id is present in the table) from the table.
So, far I am able to get all the rows corresponding to all the user_ids using this following query:
#ids = [1,2,3]
#locations = Location.where(user_id: ids)
How can I modify this activerecord query so that it returns only the last row corresponding to each user_id
Assuming you have a User model that has many locations, you could start from the user model and use the association to get to your last location for each user.
User.where(:id => #ids).includes(:locations).collect { |u| u.locations.last }
User.where(:id => #ids) returns your collection of user objects.
includes(:locations) eager loads the associated locations, so we don't run into an n+1 problem.
collect { |u| u.locations.last } maps the last associated location into an array
You can also try this:
#ids = [1,2,3]
#locations = #ids.map {|id| [Location.where(user_id: id).last.location,User.find(id).name]}
#This would give you something like this: [["US",XYZ],["IND","ABC"]]

How to create a special order_number that is based off the table id column in PostgreSQL and Rails

I have an Orders table in a SQL database (PostgreSQL if it matters, and Rails 4.04) and I want the column "number" in the orders table to kind of shadow the id column. In other words I want an automatic sequential field but I want it to be in the form:
Order::PRODUCT_NU_PREFIX + ((Order::STARTING_PRODUCT_NO + order.id).to_s)
So that if I set
PRODUCT_NU_PREFIX = 'PE' and
STARTING_PRODUCT_NO = '11681'
Then the first order I create will have a product number:
KB11682 and then
KB11683
SHould I do this purely in PostgreSQL or is there a good way to do it in Rails? Keep in mind I'll need to know what the latest Order.id is when an order comes in because when I save that new record I want that field to get saved correctly.
You're looking at uuid's (Universally Unique Identifiers)
These basically allow you to assign a special id to a record, giving you an instant reference throughout your app. Such use cases for this include the likes of order numbers, API keys and message ID's
We actually implement what you're asking quite regularly:
#app/models/order.rb
Class Order < ActiveRecord::Base
before_create :set_num
private
def set_num
unless self.num
loop do
token = SecureRandom.hex(10)
break token unless self.class.exists?(num: token)
end
end
end
end
orders
id | num | other | attrs | created_at | updated_at
Rails has a series of built-in UUID-generators:
SecureRandom.uuid #=> "1ca71cd6-08c4-4855-9381-2f41aeffe59c"
SecureRandom.hex(10) # => "52750b30ffbc7de3b362"
SecureRandom.base64(10) # => "EcmTPZwWRAozdA=="

finding records where belongs_to table's column is not equal

I have a Trainer model that has a has_many relationship with Pokemon. How do I find all Trainers who do not have any Pokemon of a certain type or group of types(type is a column in Pokemon model)?
The code I have tried, but it returns a trainer if any of his Pokemon's types is not in the group (e.g. if a trainer has pokemon of type fire and electric, he will be returned because electric isn't in the array. I don't want him returned because he has a fire pokemon.)
Trainer.joins(:pokemons).where("pokemons.type NOT IN (?)", ["fire","grass","water"])
The same problem occurs when I'm just comparing to one type.
Trainer.joins(:pokemons).where("pokemons.type != ?", "fire")
Again, the example trainer will get returned because he has a pokemon with a type, electric, that is not equal to fire.
I am using Rails 3.2.13 and Ruby 1.9.3.
I don't think there is a way to write this with Rails in one query, but you can do the following:
Trainer.where('trainers.id NOT IN (?)', Pokemon.where(type: ['grass', 'fire', 'water']).pluck(:trainer_id).uniq )
Explained version:
# selects the IDs of Trainer having a Pokemon of type Grass||Fire||Water
trainer_ids = Pokemon.where(type: ['grass', 'fire', 'water']).pluck(:trainer_id)
# returns the trainers
Trainer.where('trainers.id NOT IN (?)', trainer_ids)
In Rails 4, you can do:
Trainer.where.not(id: Pokemons.select(:trainer_id).where("pokemons.type IN (?)", ["fire","grass","water"])
This will grab all the trainers where their id does not show up in the list of trainer_ids on pokemons with fire, grass, or water.
An alternative is doing the query on the SQL layer through ActiveRecord's find_by_sql:
Trainer.find_by_sql [
"SELECT * FROM trainers
WHERE id NOT IN
(SELECT DISTINCT(trainers.id) FROM
trainers JOIN pokemons
ON trainers.id = pokemons.trainer_id
WHERE pokemons.type IN ('grass', 'fire','water'))"
]

Rails returns a nil, when searching with find_by

I'm beginning to learn RoR, but i've a problem which i don't understand. With Product.find :all returns all the records from DB. But if i want to find_by_gender(1) (or even 2) it returns a nil, i'm certain that the db contains products with a gender
My code controller:
gender = params[:gender].to_i
#search_results = Product.find_by_gender(gender)
this returns a nill,
What am i doing wrong?
Greetings!
find_by_... returns either first record or nil if none found, find_all_by_... returns all records that match (or empty array if none). In your case nil means no records found with gender = 1.
Verify your data first!
Look at some sample records:
Do something like:
Product.all(:limit => 5).each {|product| product.id.to_s + product.gender}
or go into sql
sql> select id, gender from products where id < 6;
If you are to verify what the gender values are you can then create named scopes in your model for those conditions, e.g. (rails3)
(Product Model - app/models/product.rb)
scope :male where(:gender) = male_value # i.e. 1 or 'M' or 'Male' or whatever
scope :female where(:gender) = female_value # i.e. '2' or 'F' or whatever
Which will then you let write Products.male or Products.female !
Final note - should gender be in your users table? , or is this for male / female specific products?
in rails console execute
Product.pluck(:gender)
And u will know that values does it have in AR(i think true and false), so u have to use query Product.find_by_gender(true)

Resources