Creating join table vs manually defining? - ruby-on-rails

The app I am working on is a print shop that prints items and each item can have multiple locations with each location having it's own pricing to add onto the final cost of the item.
I'm doing something like this in the create method:
if (#product.front_print && #product.back_print).present?
#product.production_price = (#product.price.to_i + 5)
else
#product.production_price = #product.price
end
price is base price. production_price is final cost of goods.
This is an example where if there is more than one print location (ie, front + back), it will generate $5 extra on top of the base price to the final production_price.
The final situation will have 6 possible print locations.
Should i be creating a join table for something like this with the join table having a Product model, and PrintLocation model?
The PrintLocation model would have ID, Title, and Price.
Is this ideal or what would be the best route to go about doing this?

Related

Searching for specific characters in user input

Within my house table I have a postcode for each house.
I also have an index view for my housing table that contains a table which contains headings such as 'Name', 'Address', 'State'. I was looking to integrate a text_field_tag that would allow user's to input the 9 digits of a postcode in order to filter the table to only show the house with that postcode. However, I also want the user to be able to input the first 4 digits of their postcode e.g. '7644' and it would display all houses that begin with '7644' e.g. two records one with the postcode of the '76444-5645' and '76443-123'. Ideally I would apply logic through my '#search' variable within my houses controller. However I am up to any ideas or tips.
In order to instantiate the house model I would use #house = House.all
I'll be honest I don't know where to begin with this. I have arel_sql in my system so I assume that would be used to query for the search.
It depends on how your models/controllers are defined but you're probably looking for the SQL operator LIKE + '%', which allows you to search for a pattern in a given column. Example:
LIKE Operator
Description
WHERE CustomerName LIKE 'a%'
Finds any values that start with "a"
Assuming you're using ActiveRecord and your model is House, it wouldn't event need to instantiate all houses. Your code would look something like this:
postcode = '7644'
#houses = House.where('postcode LIKE ?', "#{postcode}%") # this returns where the postcode starts with '7644'
another similar SO answer for reference

Ecommerce (Spree) print count of products with completed orders on Index page. (postgres)

On our product gallery page, I'd really like to query the database to find how many of each product has been purchased, and print that number on the page (for admin users only). I feel like I have all the pieces, I just don't know how to put it together to work.
There is a Products table that is referenced to print out all the products.
There is an LineItems table (Spree::LineItems >> Spree::Orders) where the product can be called/identified by:
#line_items.each do |line_item|
line_item.product.id
end
& this is what defines an order as complete in relation to that line_item:
#line_item.order.state == "complete"
So...I'd like to see if #product.id and #line_item.product.id match (where the #line_item.order.state == "complete") and count how many.
Basically, we iterate each product on the gallery page, so I want to see for each product how many times it appears as a line_item in an order where the order.state is complete
I'm no engineer (as you can obviously tell), but I feel like I'm making this more complicated than it needs to be. Help?
We're using a Postgres database. Thanks in advance!!
That should give you a count of line items associated at the same time with a product with the given id and an order with 'complete' state.
LineItem.joins(:product, :order).where(orders: { state: 'complete' }).where(products: { id: your_product_id }).count
No need to iterate! Just a database query (using ActiveRecord), which also is extremely more efficient than iteration.
I assumed your tables are called orders, products (used in the where clauses) and the associations on the LineItem ActiveRecord object are called order, product (used in the join). Otherwise simply replace those values with the appropriate ones.

Search and group products by shops REDIS

I found this question about Group By in redis but actually does not solve my issue. I have a complex search of products, once I got the ones I am looking for I want to group them by their shops, because they must be showed in a map.
My actual implementation is as follow:
-A function which search products by a pattern, it return products ids as "product:id"
product_ids = search_products_by_indexing(pattern)
-A hash with name "selling" which contains product:id/shop:id as key/value.
shops = $redis.hmget("selling", *product_ids)
# this returns list of shops as "shop:id" which sell the given prodcuts
-Then I do an intersection of shops with another list to get only shops located in a given city.
result_shops = shops & $redis.smembers("shops:city_name")
# OR by redis
$zinterstore(tem_id, shops, $redis.smembers("shops:city_name"))
result_shops = $redis.zrange(temp_id, 0, -1)
-The only thing I still need is to get the searched products grouped by result_shops. or for example this could be a hash as shop:id/[product:id] as key/value (this is the final result, shop must be in the city and product match the pattern)
Is my solution suitable to this problem or maybe there is a better implementation to solve it? any suggestion will be very appreciated!!
UPDATE: One product belongs to only one shop and one shop can have many products.
-A hash with name "selling" which contains product:id/shop:id as key/value.
This usage of a Hash will only allow you to one shop:id per product:id, meaning only one shop can sell a given product... perhaps the value should be a concatenated list of shop:ids or even better - use a Set selling:product:id and store all your relevant shop:ids in it.
-Then I do an intersection of shops with another list to get only shops located in a given city.
IMO this is redundant as the intersect's results is always shops:city_name
-The only thing I still need is to get the searched products grouped by result_shops.
If you've taken my Set instead of a Hash suggestion, this can be done with:
ZINTERSTORE tem_id 2 shops:city_name selling:product:id
ZRANGE tem_id 0 -1

Ruby on Rails: Return name if table value is between a specified range

I have three tables (that are relevant to this problem). One table is called organizations.
I also have a table called organization_details, which contains organization_id and multi-row information about the organization.
I work in the event industry, so the organization_details table contains a column called total_attendance, where a person can input an integer of the org's attendance for a certain year.
The third table is called divisions. This has five rows total, with columns division_smallest and division_largest (referring to the attendance range). Each row has a range to separate which division an organization should belong to according to their most recent attendance record.
For example, one row in the division table shows a division_smallest equal to 1 and a division_largest equal to 100000 (again, referring to attendance). Finally, the division table also has a name column (e.g. "Division 1").
I want the app to automatically figure out which division an organization belongs to according to their most recent total_attendance. Ideally, the division's name would display in the organization index and show pages.
I'd like to make a custom method for this, but am unsure how best to tackle it. I've read a little bit about .between? as in (possibly) .between?(division.division_smallest, division.division_largest) return "#{division.name}"
...But I am not sure how the entire method would work or if I need to steer away from that entirely. I would greatly appreciate any insight into this!
My suggestion is to add the following method to organization.rb
def division_name
last_details = organization_details.order('created_at DESC').first
if last_details.present?
Division.where(':attendance >= division_smallest AND :attendance <= division_largest', attendance: last_details.attendance).first.name
else
"None"
end
end
The code first grabs the organization details that have been created most recently. If the organization has organization details it uses the attendance value to select the appropriate division and it returns that division's name. If the organization doesn't have any organization_details it returns the string "None". You may also want to handle the case where the attendance isn't inside of the range on any of the divisions you have defined.
I hope this points you in the right direction.
A naive implementation might look something like this:
class Division
def self.for_attendance(total)
first('? BETWEEN divisions.division_smallest AND divisions.division_largest', total)
end
end
class Organization
def latest_division
Division.for_attendance(organization_details.last.try(:total_attendance))
end
end
Now calling some_organization.latest_division will pull the latest division for that organization. This is great for a 'show' page, but will run you into trouble when you have an 'index' with many Organizations - these 2 queries will need to run for each Organization (an N+1 problem). Instead use this:
class Division
def self.merge_latest!(organizations)
left_join = "LEFT JOIN organization_details od2 ON organization_details.organization_id = od2.organization_id AND organization_details.created_at < od2.created_at"
subquery = OrganizationDetails.where(organization_id: organizations.map(&:id)).
joins(left_join).
where(od2: {id: nil}).to_sql
divisions = joins("#{subquery} as t ON t.total_attendance divisions.division_smallest AND divisions.division_largest").
select('divisions.*, t.organization_id')
organizations.each {|org| org.latest_division = divisions.detect{|d| d.organization_id == org.id}
end
end
def Organization
attr_accessor :latest_division
end
Now you can call Division.merge_latest!(organizations) to collect the latest division for all the organizations in a single query, addressable via an organization's :latest_division attribute.

How do I find where id does not match any of an array?

I have a Workout model that has and belongs to many Equipment models. I have an array of some Equipment IDs. I want to find all Workouts that don't have any Equipment assigned that matches any of the array of Equipment IDs.
So, if my array = [2,3,5] I want to find all workouts where the assigned equipment ids does not include 2, 3 or 5.
EDIT:
Workout.joins(:equipment).where("equipment.id not in(?)",[2,3,5]).uniq
Assuming five instances of Equipment, the code above returns workouts with equipment.ids 1 and 4 (good), but also returns partial matches for example Workouts with equipment.id = [1,2], [1,2,3].
It helps to think of what result set your query returns.
Workout.joins(:equipment).where("equipment.id not in(?)",[2,3,5]).uniq
Joins all the related equipments to their workouts. If a workout was linked to 4 equipments then you'd get 4 rows for that workout. The where clause just filters that 4 down to a smaller number - it can't wipe them all out just because one matches.
What you need to do instead is add conditions to the join itself. Something like
select workouts.*
left join equipments_workouts on workout_id = workouts.id and equipment_id in (2,3,5)
where equipment_id is null
Should return the correct workouts (it should also return a workout with 0 equipments but I don't know if that's a consideration.)
This works by trying to join 'bad' equipments. Because it's a left join, if no such row can be found then the result set will still include a row for that workout but with the columns for equipmnts_workouts all set to null. As a bonus you no longer have to eliminate duplicates.
Activerecord doesn't have a very nice way of writing queries like this. The joins method will accept an arbitrary SQL fragment though:
Workout.joins("left join equipment_workouts on workout_id = workouts.id and equipment_id in (2,3,5)").
where("equipment_id is null")
You might find the sanitize_sql method useful for generating that sql fragment
Workout.joins(:equipment).merge(Equipment.where("id not in(?)",[2,3,5])).uniq
or
Workout.joins(:equipment).where("equipments.id not in(?)",[2,3,5]).uniq
also u can try this, it should find all Workouts that don't have any Equipment
Workout.includes(:equipment).where("equipments.id not in(?)",[2,3,5])
This can be improved, but should work:
class Workout < ActiveRecord::Base
scope :without_equipments, lambda{|ids| joins(:equipment).where("equipments.id not in (?)", ids.repeated_permutation(ids.size).map(&:uniq).uniq)}
end
Workout.without_equipments 2,3,5

Resources