-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Methods for Spree::Taxon for all products/variants from descendants #1761
Methods for Spree::Taxon for all products/variants from descendants #1761
Conversation
core/app/models/spree/taxon.rb
Outdated
end | ||
|
||
# @return [ActiveRecord::Relation<Spree::Variant>] all self and descendant variants, including master variants. | ||
def all_variants |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can get cleaned up to
Variant.where(product_id: all_products.pluck(:id))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbrunsdon I will change that, my original thinking for the other way was to save database calls but it only saves us one db call. Thank you for your feedback.
core/app/models/spree/taxon.rb
Outdated
@@ -79,12 +79,32 @@ def to_param | |||
permalink | |||
end | |||
|
|||
# @return [Array<Integer>] the ids of descendant taxons. | |||
def descendant_ids |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is really worth being a public method, its more likely anyone that wants this will not know it exists and call descendants.pluck(:id)
f498768
to
50833f7
Compare
core/app/models/spree/taxon.rb
Outdated
@@ -85,6 +85,16 @@ def active_products | |||
products.not_deleted.available | |||
end | |||
|
|||
# @return [ActiveRecord::Relation<Spree::Product>] all self and descendant products | |||
def all_products | |||
Product.joins(:taxons).where(spree_taxons: { id: descendant_ids.push(id) }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to use descendant_ids + [id]
.
Push would append to the array descendant_ids
returns which we want to avoid (I don't know whether or not that would cause a real issue, but I can't know without reading its source, so we want to avoid it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good eye. Changing now. Thanks.
4812786
to
57559b9
Compare
@jhawthorn @cbrunsdon I just wanted to checkup on the state of this PR. Thank you guys. |
@jhawthorn @cbrunsdon Is there any update on this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Dustin,
Sorry for the slow response on this one. I have some feedback in regards to query optimization that I think makes sense here.
core/app/models/spree/taxon.rb
Outdated
@@ -85,6 +85,16 @@ def active_products | |||
products.not_deleted.available | |||
end | |||
|
|||
# @return [ActiveRecord::Relation<Spree::Product>] all self and descendant products | |||
def all_products | |||
Product.joins(:taxons).where(spree_taxons: { id: descendant_ids + [id] }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some query optimizations I think would be suitable here. Any time we use pluck, we take back the identifiers and put them in a ruby array which means a round trip to the database.
Currently all_products
is required to do 2 queries, and all_variants
is required to do 3, but we can reduce that to one each pretty easily.
Example of current behaviour:
[1] pry(main)> Spree::Taxon.first.all_variants.count
Spree::Taxon Load (0.1ms) SELECT "spree_taxons".* FROM "spree_taxons" ORDER BY "spree_taxons"."id" ASC LIMIT ? [["LIMIT", 1]]
(0.2ms) SELECT "spree_taxons"."id" FROM "spree_taxons" WHERE ("spree_taxons"."lft" >= 1) AND ("spree_taxons"."lft" < 12) AND ("spree_taxons"."id" != 1) ORDER BY "spree_taxons"."lft"
(0.2ms) SELECT "spree_products"."id" FROM "spree_products" INNER JOIN "spree_products_taxons" ON "spree_products_taxons"."product_id" = "spree_products"."id" INNER JOIN "spree_taxons" ON "spree_taxons"."id" = "spree_products_taxons"."taxon_id" WHERE "spree_products"."deleted_at" IS NULL AND "spree_taxons"."id" IN (3, 4, 5, 6, 7, 1)
(0.1ms) SELECT COUNT(*) FROM "spree_variants" WHERE "spree_variants"."deleted_at" IS NULL AND "spree_variants"."product_id" IN (1, 2, 8, 9, 4, 3, 5, 7, 6)
But if we try this:
# @return [ActiveRecord::Relation<Spree::Product>] all self and descendant
# products
def all_products
scope = Product.joins(:taxons)
scope.where(
spree_taxons: { id: descendants.select(:id) }
).or(scope.where(spree_taxons: { id: id } ))
end
# @return [ActiveRecord::Relation<Spree::Variant>] all self and descendant
# variants, including master variants.
def all_variants
Variant.where(product_id: all_products.select(:id))
end
Everything can be done in a single subquery:
Spree::Taxon Load (0.2ms) SELECT "spree_taxons".* FROM "spree_taxons" ORDER BY "spree_taxons"."id" ASC LIMIT ? [["LIMIT", 1]]
(0.3ms) SELECT COUNT(*) FROM "spree_variants" WHERE "spree_variants"."deleted_at" IS NULL AND "spree_variants"."product_id" IN (SELECT "spree_products"."id" FROM "spree_products" INNER JOIN "spree_products_taxons" ON "spree_products_taxons"."product_id" = "spree_products"."id" INNER JOIN "spree_taxons" ON "spree_taxons"."id" = "spree_products_taxons"."taxon_id" WHERE ("spree_products"."deleted_at" IS NULL AND "spree_taxons"."id" IN (SELECT "spree_taxons"."id" FROM "spree_taxons" WHERE ("spree_taxons"."lft" >= 1) AND ("spree_taxons"."lft" < 12) AND ("spree_taxons"."id" != 1) ORDER BY "spree_taxons"."lft") OR "spree_products"."deleted_at" IS NULL AND "spree_taxons"."id" = ?)) [["id", 1]]
=> 19
This means only one round trip to the DB, less work done in Ruby and hopefully happy developers!
@gmacdougall No problem and thank you for looking at this. I knew it had a few db calls but I should have done a better job of reducing those. Thank you again for looking at this and reducing those calls. |
core/app/models/spree/taxon.rb
Outdated
@@ -124,5 +137,10 @@ def touch_ancestors_and_taxonomy | |||
# Have taxonomy touch happen in #touch_ancestors_and_taxonomy rather than association option in order for imports to override. | |||
taxonomy.try!(:touch) | |||
end | |||
|
|||
# @return [Array<Integer>] the ids of descendant taxons. | |||
def descendant_ids |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is no longer used and can be removed.
@gmacdougall You are right. I removed that method and I will rebase these commits into a single commit if/when this gets approved or when prompted to. |
Looks reasonable to me. Thanks for addressing the feedback. Rebase away! |
978d749
to
9a24fd9
Compare
Thank you so much for providing feedback on this! |
@gmacdougall Just checking up on the status of this PR. Any update? |
core/app/models/spree/taxon.rb
Outdated
scope = Product.joins(:taxons) | ||
scope.where( | ||
spree_taxons: { id: descendants.select(:id) } | ||
).or(scope.where(spree_taxons: { id: id } )) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could probably be simpler as where(spree_taxons: { id: self_and_descendants.ids } )
3069036
to
568049c
Compare
I modified Thanks for the change 👍 |
This adds 2 primary methods to Spree::Taxon to allow users to retreive all products and all variants that are under the taxon instance. Helpful for a taxon page to show an overview of everything that is under that taxon.
568049c
to
b494168
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Thanks for the work on this Dustin! Sorry for the slow response. |
core/app/models/spree/taxon.rb
Outdated
# @return [ActiveRecord::Relation<Spree::Product>] the active products the | ||
# belong to this taxon | ||
def active_products | ||
products.not_deleted.available | ||
end | ||
|
||
# @return [ActiveRecord::Relation<Spree::Product>] all self and descendant products | ||
def all_products | ||
Product.joins(:taxons).where(spree_taxons: { id: descendant_ids.push(id) }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self_and_descendants is provided by acts_as_nested_sets so this can get cleaned up with
self_and_descendants.pluck(:id)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the whole thing can be done in one query with:
Spree::Product.joins(:classifications).joins(:taxons).merge(self_and_descendants).distinct
This adds 2 primary methods to Spree::Taxon to allow users to retreive
all products and all variants that are under the taxon instance. Helpful for
a taxon page to show an overview of everything that is under that
taxon. Also, included is a shortcut method for
taxon.descendants.pluck(:id)
.