Skip to content
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

Merged

Conversation

dgra
Copy link
Contributor

@dgra dgra commented Mar 8, 2017

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).

@dgra dgra changed the title Add helper methods to Spree::Taxon model for all products/variants Methods for Spree::Taxon for all products/variants from descendants Mar 9, 2017
end

# @return [ActiveRecord::Relation<Spree::Variant>] all self and descendant variants, including master variants.
def all_variants
Copy link
Contributor

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))

Copy link
Contributor Author

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.

@@ -79,12 +79,32 @@ def to_param
permalink
end

# @return [Array<Integer>] the ids of descendant taxons.
def descendant_ids
Copy link
Contributor

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)

@dgra dgra force-pushed the feature/all_products_taxon branch from f498768 to 50833f7 Compare March 15, 2017 15:37
@@ -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) })
Copy link
Contributor

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)

Copy link
Contributor Author

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.

@dgra dgra force-pushed the feature/all_products_taxon branch 2 times, most recently from 4812786 to 57559b9 Compare March 16, 2017 22:51
@dgra
Copy link
Contributor Author

dgra commented Apr 3, 2017

@jhawthorn @cbrunsdon I just wanted to checkup on the state of this PR. Thank you guys.

@dgra
Copy link
Contributor Author

dgra commented May 23, 2017

@jhawthorn @cbrunsdon Is there any update on this PR?

Copy link
Member

@gmacdougall gmacdougall left a 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.

@@ -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] })
Copy link
Member

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!

@dgra
Copy link
Contributor Author

dgra commented Jul 3, 2017

@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.

@@ -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
Copy link
Member

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.

@dgra
Copy link
Contributor Author

dgra commented Jul 3, 2017

@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.

@gmacdougall
Copy link
Member

Looks reasonable to me. Thanks for addressing the feedback.

Rebase away!

@dgra dgra force-pushed the feature/all_products_taxon branch from 978d749 to 9a24fd9 Compare July 3, 2017 23:35
@dgra
Copy link
Contributor Author

dgra commented Jul 3, 2017

Thank you so much for providing feedback on this!

@dgra
Copy link
Contributor Author

dgra commented Sep 5, 2017

@gmacdougall Just checking up on the status of this PR. Any update?

scope = Product.joins(:taxons)
scope.where(
spree_taxons: { id: descendants.select(:id) }
).or(scope.where(spree_taxons: { id: id } ))
Copy link
Contributor

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 } )

@jhawthorn jhawthorn force-pushed the feature/all_products_taxon branch 2 times, most recently from 3069036 to 568049c Compare October 4, 2017 20:09
@jhawthorn
Copy link
Contributor

I modified all_products to be a little cleaner using self_and_descendants.

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.
@jhawthorn jhawthorn force-pushed the feature/all_products_taxon branch from 568049c to b494168 Compare October 4, 2017 20:12
Copy link
Member

@gmacdougall gmacdougall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@gmacdougall
Copy link
Member

Thanks for the work on this Dustin! Sorry for the slow response.

# @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) })
Copy link
Contributor

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)

Copy link
Contributor

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

@gmacdougall gmacdougall merged commit 00bad3a into solidusio:master Oct 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants