Skip to content

Loading…

Sorting categories #10

Closed
carr opened this Issue · 10 comments

3 participants

@carr

Hello Stefan,
nice work on the ancestry gem.

I have a problem.

I have a model called "User" that has a

has_and_belongs_to_many :categories

association.

Also, let's say I have a category tree that looks like this:

+ Node1
++ Node11
++ Node12
++ Node13
++ Node14
+ Node2
+ Node3
+ Node4
++ Node41
+++ Node411
+++ Node412
++ Node42

Now, how do I get those nodes from the database for a particular user but in the order specified above?

I tried doing

@user.categories.all(:order => 'ancestry ASC, name ASC')

But since Node1, Node2, Node3 and Node4 all have the "ancestry" field set to nil, that doesn't work.

It seems like I'm missing something, but can't put my finger on it.

Thanks,
Tomislav

@stefankroes
Owner

Dear Tomislav,

For now you might want to check out the arrange method. (@user.categories.arrange)

I've also been thinking about other solutions to the same problem like using ancestry for eager loading. However, I don't know how to implement this yet.

Kind regards,

Stefan

@carr

Hey Stefan,
actually I need this to insert into a collection_select(..) form helper so the user can choose the category.

Is there a preferred method for this, or should I try to create my own helper/patch using the "arrange" method, what do you think?

Thanks,
Tomislav

@scottkf

I realize this is closed, but did you ever come up with a solution for this carr? Using arrange returns a hash, but I can't seem to manipulate it using each_pair, it keeps turning it into an array.

@carr

Hello scott, no, unfortunately didn't work on this problem..

@scottkf

This is one method I came up with: certainly not the best way to do it, but it works:

def arranged_categories
  array = Array.new
  x = lambda { |k| 
    array << [("-" * k.depth.to_i)+ " " + k.name, k.id]
    if k.has_children?
      k.children.map(&x) 
    end
  }
  Category.roots.map(&x)
  array
end

Then in the form, <%= f.select :category_id, @categories %>

@stefankroes
Owner

I guess you will have to cache the path of names in each node. Something like:

def before_save
  self.names_path_cache = path.map(&:name).join('/')
end

This will save strings like 'Node4/Node41/Node411' by which you will be able to sort.

Does this work for you?

I son't see any way to sort this list in db space without an extra column.

@scottkf

This would work better, yes. Doing something like
Category.all.sort {|x,y| x.names_path_cache <=> y.names_path_cache }.map { |c| ["-" * c.depth + c.name,c.id] } produces the correct results. Thanks!

@stefankroes
Owner

The idea of caching the names path to the db is that you can sort your list in db space:

@user.categories.order(:names_path_cache)

This is obviously more performant

@scottkf

That is very true, I will add this to the wiki, "creating a form with ancestry", as I am sure others will find it useful, if this is ok with you!

@stefankroes
Owner

Great! Let's close this ticket.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.