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

Scope for leaves #246

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

Conversation

antstorm
Copy link
Contributor

@antstorm antstorm commented Dec 5, 2015

Hi,

Here's a pure-SQL support for getting all the leaves of the tree. Basically making sure that for every record there's other record that references it's ancestry. I had to use OR in the JOIN because ancestry might be nil.

@antstorm antstorm force-pushed the leaves-scope branch 3 times, most recently from 15aef88 to 37e4a9b Compare December 5, 2015 20:19
@kbrock
Copy link
Collaborator

kbrock commented Dec 6, 2015

I'd too would like to add postgres specific version of some other methods. e.g.: sorting by nulls vs using a case statement.

At a hight level, is the code here the best way to introduce database specific sql?

@antstorm
Copy link
Contributor Author

antstorm commented Dec 6, 2015

@kbrock I'd say if more methods start to appear then it's better to extract them into an SQLHelper module or something. Would be great for adapters to support that, but I think it might be out of their scope.

@kbrock
Copy link
Collaborator

kbrock commented Jan 1, 2016

Hi @antstorm It is a long shot, but seeing if we can put this abstraction into arel here

@antstorm
Copy link
Contributor Author

antstorm commented Jan 1, 2016

@kbrock good shout, let's see what the arel guys think. Judging by the previous PR attempt they're not very positive about supporting infix operators.

@antstorm
Copy link
Contributor Author

antstorm commented Mar 8, 2016

@kbrock nice one, I never thought it would make it's way into arel! Although I don't think I can use that here, since ancestry depends on activerecord >= 3.0.0.

@ImDineshSaini
Copy link

Alternate is add a file in config/initializer

module Ancestry
  module ClassMethods
    # Scope that returns all the leaves
    def leaves
      id_column = "#{table_name}.id"
      id_column_as_text = sql_cast_as_text(id_column)
      parent_ancestry = sql_concat("#{table_name}.#{ancestry_column}", "'/'", id_column_as_text)

      joins("LEFT JOIN #{table_name} AS c ON c.#{ancestry_column} = #{id_column_as_text} OR c.#{ancestry_column} = #{parent_ancestry}").
        group(id_column).
        having('COUNT(c.id) = 0')
    end

    private

    def sql_concat *parts
      if ActiveRecord::Base.connection.adapter_name.downcase == 'sqlite'
        parts.join(' || ')
      else
        "CONCAT(#{parts.join(', ')})"
      end
    end

    def sql_cast_as_text column
      text_type = if ActiveRecord::Base.connection.adapter_name.downcase == 'mysql2'
        'CHAR'
      else
        'TEXT'
      end

      "CAST(#{column} AS #{text_type})  collate utf8_unicode_ci"
    end
  end
end

@kbrock
Copy link
Collaborator

kbrock commented Oct 7, 2016

@antstorm Arel supports concat (see reference above to rails PR)
I'd like to release ancestry v3 with rails 4.x support and above.
And then start to use stuff like this.

Ancestry 2.1.x can support older rails, just not get these newer features.
Are you still using ancestry? What version of rails do you need to support?

@antstorm
Copy link
Contributor Author

antstorm commented Oct 7, 2016

@kbrock I'm not using ancestry in my current projects, but I'm happy to shape this PR into something mergeable, dropping rails < 4 support.

@kbrock kbrock self-assigned this May 29, 2018
@fursich fursich mentioned this pull request Jul 10, 2018
@fursich
Copy link
Contributor

fursich commented Jul 10, 2018

Hi @antstorm , would you be comfortable if I rebase this PR and give it a new life, since I found it much better than mine (#388) in terms of SQL efficiency and simplicity.
As a daily user of ancestry I appreciate the efforts made here, and leaving this feature un-merged is just a shame..

(@kbrock I'm assuming currently nobody is working on it, let me know if I'm in a totally wrong place.. you might already have clear ideas about what to be done.)

Hope that makes sense 😸

@antstorm
Copy link
Contributor Author

@fursich sure, I've rebased it. Do you think there's anything missing from it?

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

6 similar comments
@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling e84a651 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

@coveralls
Copy link

coveralls commented Jul 10, 2018

Coverage Status

Coverage increased (+0.08%) to 98.187% when pulling 7e53201 on antstorm:leaves-scope into 91b3ace on stefankroes:master.

Copy link
Collaborator

@kbrock kbrock left a comment

Choose a reason for hiding this comment

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

This looks good

@@ -36,5 +36,5 @@ services:
- postgresql

before_script:
- mysql -e 'create database ancestry_test;' || true
- psql -c 'create database ancestry_test;' -U postgres || true
- mysql -e 'CREATE DATABASE ancestry_test CHARACTER SET utf8 COLLATE utf8_general_ci;' || true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hi @antstorm

I think this line is significant. Could you please explain more about this?

We may want to add this to the general documentation to help people.
Also if this is not changed as the database collation, then I wonder what users can do to get this behavior.

I'm also thinking postgres on mac has a similar issue with this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

or maybe this line should be collation on just the column if they don't do this to the whole database?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I added collation to the column and not to the whole database.
Thanks for putting this on my radar.

@@ -17,6 +17,17 @@ def scope_depth depth_options, depth
end
end

# Scope that returns all the leaves
def leaves
id_column = "#{table_name}.id"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
id_column = "#{table_name}.id"
id_column = "#{table_name}.#{self.class.primary_key}"


joins("LEFT JOIN #{table_name} AS c ON c.#{ancestry_column} = #{id_column_as_text} OR c.#{ancestry_column} = #{parent_ancestry}")
.group(id_column)
.having('COUNT(c.id) = 0')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
.having('COUNT(c.id) = 0')
.having("COUNT(c.#{self.class.primary_key}) = 0")

id_column_as_text = sql_cast_as_text(id_column)
parent_ancestry = sql_concat("#{table_name}.#{ancestry_column}", "'/'", id_column_as_text)

joins("LEFT JOIN #{table_name} AS c ON c.#{ancestry_column} = #{id_column_as_text} OR c.#{ancestry_column} = #{parent_ancestry}")
Copy link
Collaborator

Choose a reason for hiding this comment

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

this looks like "children" to me.
Think we may want "subtree" instead?

Copy link
Collaborator

Choose a reason for hiding this comment

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

but it seems to be working.

Could you just give your opinion on what you think about this line and multiple levels of nested members?

Thnx

@@ -6,6 +6,9 @@ def test_scopes
# Roots assertion
assert_equal roots.map(&:first), model.roots.to_a

# Leaves assertion
assert_equal model.all.select(&:is_childless?), model.leaves.order(:id).to_a
Copy link
Collaborator

Choose a reason for hiding this comment

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

well, this does look like it is bringing back nodes from 2 levels deep.

ASIDE (nothing to do with you or this PR):
The way the tests are organized, it sure makes it hard to know where different elements are tested.

@kevinluo201 kevinluo201 mentioned this pull request Oct 22, 2020
@iainbeeston
Copy link

Is there any chance of resurrecting this? What else needs doing for it to be mergeable?

@kbrock
Copy link
Collaborator

kbrock commented Aug 30, 2022

Do you want to put together another PR?

We now have a postgres only mixin, so we could do the same style for mysql and postgres to get past the database specific concat syntax. Which I think was the only issue.

I did find an old gem called ancestry_joins which seems to do similar stuff like this as well. Maybe we could compare the 2 approaches and find one that works best for us

@kbrock
Copy link
Collaborator

kbrock commented Mar 4, 2023

@iainbeeston sorry for the delay here.

Yes, this is still on my radar.
I'm working at finding a way to drop to_node so we can use scopes in our queries and make this work well.

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.

6 participants