How to search Spree::Orders and associations with ransack gem - ruby-on-rails-3

Using Spree and ransack, how is it possible to return the Spree::Variants included in Spree::Orders that have completed since a certain date? The ransack documentation gives a little advice about searching associations, but does not seem to go deep enough.
Spree::Order
has_many :line_items
Spree::LineItem
belongs_to :line_item
belongs_to :variant
Spree::Variant
has_many: line_items
Orders that have completed can be searched with:
o=Spree::Order.complete.ransack(completed_at_gt: '2015-05-01')
But how to find the Spree::LineItems within these orders and return the Spree::Variants' SKUs within these orders is unclear.
I'm attempting to use ransack because this will be used in a Spree report.

You don't need to use ransack in this area at all. ActiveRecord will let you do what you need:
Spree::Order.complete.where(
Spree::Order.arel_table[:completed_at].gteq(
Date.new(2015, 10, 1)
)
).joins(
line_items: :variant
).select(:sku).distinct.pluck(:sku)
If you must use ransack, you can get the ActiveRecord relation, using .result and apply the joins and such.
Spree::Order.complete.ransack(
completed_at_gt: '2015-05-01'
).result.joins(
line_items: :variant
).select(:sku).distinct.pluck(:sku)
Since you don't have .pluck, you're running a really old version of Spree that is pre-Rails 4. You can get the results by using:
Spree::Order.complete.ransack(
completed_at_gt: '2015-05-01'
).result.joins(
line_items: :variant
).select(:sku).map(&:sku).uniq

Related

Rails, gem with functionality in between enum and many to many

i have im my rails app model which has few options (no more than 10 i think).
Something like Product - Category, where product can be part of 1 or many categories.
But i think i have too few categories to engage fully fledged many-to-many construct.
Moreover the list of categroies is predefined and will almost never change.
I think from sql side this could look like string field categories with such content:"Fruits|Vegetables|..."
Maybe someone know preexisting gem for such functionality, or maybe it is no real advantage doing so and i should choose standart many-to-many ?
I checked acts-as-taggable-on plugin, but it is i think fits not very well for this task.
Enum gems like enumerize i think fit just best, but they are allow only single single value to be choosen.
Currently came out with following combination:
This gem:
https://github.com/pboling/flag_shih_tzu
In model:
class Product < ActiveRecord::Base
KINDS = { 1 => :fruit, 2 => :vegetable }
include FlagShihTzu
attr_accessible *KINDS.values
as_flags KINDS
Then in view (haml):
=form_for [#product] do |f|
-Product::KINDS.each do |k, v|
=f.check_box v
=f.label v
UPDATE:
Yet another gem adressing this problem: https://github.com/joelmoss/bitmask_attributes

putting a condition on an includes

I have the following relationships:
Category has_many :posts
Post has_many :comments
Post has_many :commenters, :through => :comments
I have the following eager load, giving me posts, comments and commenters (note that I need all 3, and hence the includes as opposed to joins)
category.posts.includes(:comments, :commenters)
However, I'd like to limit comments (and if possible commenters) to only those created in the past two weeks while still returning the same set of posts. Initially I thought I could specify a condition on the includes:
category.posts.includes(:comments, :commenters).where("comments.created_at > ?", 2.weeks.ago)
But found that this returns only the posts that meet the condition. I'm thinking that I may need to do something like performing a subquery on comments and then performing a join. Is there an easy way to do this with AR of would I be better off doing this with sql?
Finally managed to figure this out from reading this page:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
I simply needed to create an association in my Post model like:
Post has_many :recent_comments, :class_name = 'Comment', :conditions => ["created_at > ?", 2.weeks.ago]
Then I could do the following to get the desired ActiveRecord::Association object:
category.posts.includes(:recent_comments => :commenters)
There was also a suggestion of doing this by using a scope on a model. However, I read somewhere (I think it was on SO) that scopes are on their way out and that ARel has taken their place so I decided to do this without scopes.
Try :
category.posts.all(:includes => {:comments =>:commenters}, :conditions => ["comments.created_at = ? AND commenters.created_at = ?", 2.weeks.ago, 2.weeks.ago]

How to retrieve both "associated" records and "associated through" records in a performant way?

I am using Ruby on Rails 3.1 and I am trying to improve an SQL query in order to retrieve both "associated" records and "associated through" records (ActiveRecord::Associations) in a performant way so to avoid the "N + 1 query problem". That is, I have:
class Article < ActiveRecord::Base
has_many :category_relationships
has_many :categories,
:through => :category_relationships
end
class Category < ActiveRecord::Base
has_many :article_relationships
has_many :articles,
:through => :article_relationships
end
In a couple of SQL queries (that is, in a "performant way", maybe by using the Ruby on Rails includes() method) I would like to retrieve both categories and category_relationships, or both articles and article_relationships.
How can I make that?
P.S.: I am improve queries like the followings:
#category = Category.first
articles = #category.articles.where(:user_id => #current_user.id)
articles.each do |article|
# Note: In this example the 'inspect' method is just a method to "trigger" the
# "eager loading" functionalities
article.category_relationships.inspect
end
You can do
Article.includes(:category_relationships => :categories).find(1)
Which will reduce this to 3 queries (1 for each table). For performance, also make sure your foreign keys have an index.
But in general, I'm curious why the "category_relationships" entity exists at all, and why this isn't a has_and_belongs_to sort of situation?
Updated
As per your changed question, you can still do
Category.includes(:article_relationships => :articles).first
If you watch the console (or tail log/development) you'll see that when you call the associations, it'll hit the cached values and you're golden.
But I am still curious why you're not using a Has and Belongs To Many association.

Constructing a has-and-belongs-to-many query

I have a rails app (running on version 2.2.2) that has a model called Product. Product is in a has-and-belongs-to-many relationship with Feature. The problem is that I need have search functionality for the products. So I need to be able to search for products that have a similar name, and some other attributes. The tricky part is that the search must also return products that have the exact set of features indicated in the search form (this is represented by a bunch of checkboxes). The following code works, but it strikes me as rather inefficient:
#products = Product.find(:all, :conditions=>["home=? AND name LIKE ? AND made_by LIKE ? AND supplier LIKE ? AND ins LIKE ?",hme,'%'+opts[0]+'%','%'+opts[1]+'%','%'+opts[3]+'%','%'+opts[4]+'%'])
#see if any of these products have the correct features
if !params[:feature_ids].nil?
f = params[:feature_ids].collect{|i| i.to_i}
#products.delete_if {|x| x.feature_ids!=f}
end
I'm sorry that my grasp of rails/sql is so weak, but does anyone have any suggestions about how to improve the above code? Thanks so much!
First, i would recommend you to manually write a FeatureProduct model (and not use the default 'has_and_belongs_to_many')
EG
class FeatureProduct
belongs_to :feature
belongs_to :product
end
class Product
has_many :feature_products
has_many :features, :through => :feature_products
end
class Feature
has_many :feature_products
has_many :products, :through => :feature_products
end
For the search: You may find the gem SearchLogic to be exactly what you need. It has support for 'LIKE' conditions (it means that you can write in a more 'Rails way' your query). It also has support for performing a search with conditions on a related model (on your Feature model, to be more precise).
The solution would be something like:
search = Product.search
search.name_like = opt[0]
search.made_by_like = opt[1]
...
search.feature_products_id_equals = your_feature_ids
..
#product_list = search.all
There is also an excellent screencast explaining the use of this gem.
Good luck :)

Rails ActiveRecord Problem With Complex Join - Select Does Not Work

I have two model objects:
Event
Venue
Events have Venues. Venues can have 1..* events.
Venues have a location, a lat and long, which I use with the Geokit Rails plugin. Here's what these models look like in Rails:
class Event < ActiveRecord::Base
belongs_to :venue
acts_as_mappable :through => :venue
end
and
class Venue < ActiveRecord::Base
has_many :events
acts_as_mappable
end
Very simple! Now, I'd like to run a query, I'd like to find all the events within a few miles of a certain area, looking at the Geokit API, I see that I can use mappable with a :through association on Event, and should be able to call geokit finders on Event! Excellent (so you can see above, I've added acts_as_mappable :through to the Event).
Here's my ActiveRecord query:
Event.find(:all, :origin => [lat, lng], :select => 'title', :within => 5, :limit => 10)
Here's the SQL generated:
SELECT `events`.`id` AS t0_r0, `events`.`title` AS t0_r1, `events`.`description` AS t0_r2,
`events`.`created_at` AS t0_r3, `events`.`updated_at` AS t0_r4, `events`.`venue_id` AS
t0_r5, `events`.`event_detail_id` AS t0_r6, `events`.`event_detail_type` AS t0_r7,
`venues`.`id` AS t1_r0, `venues`.`name` AS t1_r1, `venues`.`lat` AS t1_r2, `venues`.`lng`
AS t1_r3, `venues`.`created_at` AS t1_r4, `venues`.`updated_at` AS t1_r5 FROM `events` LEFT
OUTER JOIN `venues` ON `venues`.id = `events`.venue_id WHERE
(((venues.lat>51.4634898826039 AND venues.lat>51.5533406301823 AND venues.lng>-
0.197713629915149 AND venues.lng<-0.053351862714855)) AND
((ACOS(least(1,COS(0.898991438708539)*COS(-0.00219095974226756)*COS(RADIANS(venues.lat))*COS(RADIANS(venues.lng))
COS(0.898991438708539)*SIN(-
0.00219095974226756)*COS(RADIANS(venues.lat))*SIN(RADIANS(venues.lng))
SIN(0.898991438708539)*SIN(RADIANS(venues.lat))))*6376.77271)
<= 5)) LIMIT 10
Ouch. Well, this takes quite some time to run, and it's not just the math, I think there's a significant problem here with performance, this takes 2 seconds to run on my desktop! But getting down to it, here is my complaint:
To try and improve performance, you can see I have tried to use a SELECT statement in my ActiveRecord find query. I just want to know the TITLES of these Events that match, not all the other fields. But the query goes through, and SELECTs everything from Event! It uses strange aliases which I'm not familiar with like 't1_r1'. So this clearly is a problem.
Furthermore, this query, when run against the Venues alone (i.e. doing away with the JOIN) is executed in less than 10ms. So is there a way to do this Venues search first, and THEN do the join against the Events? I think the join must be being done on the two tables as a whole, and only then the geocoding part comes in and slims the dataset down.
Any help in tightening up this issue would be much appreciated (this is a non-commercial project). I feel that the model is very simple I should be able to do this without too much fuss?
Thanks in advance!
(Edit: In an early version of this post I was stupid, Limit worked fine, my view was broken, edited to take that part out.)
It looks like acts_as_mappable is using the eager loading functionality of Rails Active Record. Once you start eager loading any :select argument is ignored and you cannot specify particular columns from a table/model. In your case i think this is happening because of the :through=>:venue argument to acts_as_mappable. Can you make it work without the through argument? You can always poke around the source code as well to see if there are any other adjustments you can make.