3

I have the following Models:

class Group < ActiveRecord::Base
    has_many :threads, :dependent => :destroy

class Thread < ActiveRecord::Base
    has_many :comments, :as => :commentable, :dependent => :destroy

 class Comment < ActiveRecord::Base
    belongs_to :commentable, :polymorphic => true
    acts_as_nested_set

The issue I'm having is when a user deletes a group, all kinds of comments are being broken or deleted. I took a look at the logs and this is what's happening:

Comment Load (0.8ms)  SELECT "comments".* FROM "comments" WHERE ("comments".commentable_id = 101 AND "comments".commentable_type = 'Thread') ORDER BY comments.created_at DESC
AREL (0.9ms)  DELETE FROM "comments" WHERE ("comments"."lft" > 649 AND "comments"."rgt" < 650)
AREL (0.4ms)  UPDATE "comments" SET "lft" = ("lft" - 2) WHERE ("lft" > 650)
AREL (0.5ms)  UPDATE "comments" SET "rgt" = ("rgt" - 2) WHERE ("rgt" > 650)
AREL (0.2ms)  DELETE FROM "comments" WHERE ("comments"."id" = 381)
AREL (0.4ms)  DELETE FROM "comments" WHERE ("comments"."lft" > 645 AND "comments"."rgt" < 646)
AREL (0.4ms)  UPDATE "comments" SET "lft" = ("lft" - 2) WHERE ("lft" > 646)
AREL (0.4ms)  UPDATE "comments" SET "rgt" = ("rgt" - 2) WHERE ("rgt" > 646)
AREL (0.2ms)  DELETE FROM "comments" WHERE ("comments"."id" = 380)
AREL (0.3ms)  DELETE FROM "comments" WHERE ("comments"."lft" > 648 AND "comments"."rgt" < 651)
AREL (0.3ms)  UPDATE "comments" SET "lft" = ("lft" - 4) WHERE ("lft" > 651)
AREL (0.3ms)  UPDATE "comments" SET "rgt" = ("rgt" - 4) WHERE ("rgt" > 651)
AREL (0.2ms)  DELETE FROM "comments" WHERE ("comments"."id" = 379)
AREL (0.3ms)  DELETE FROM "comments" WHERE ("comments"."lft" > 644 AND "comments"."rgt" < 647)
AREL (0.4ms)  UPDATE "comments" SET "lft" = ("lft" - 4) WHERE ("lft" > 647)
AREL (0.4ms)  UPDATE "comments" SET "rgt" = ("rgt" - 4) WHERE ("rgt" > 647)
AREL (0.2ms)  DELETE FROM "comments" WHERE ("comments"."id" = 378)
AREL (0.4ms)  DELETE FROM "comments" WHERE ("comments"."lft" > 642 AND "comments"."rgt" < 643)
AREL (0.8ms)  UPDATE "comments" SET "lft" = ("lft" - 2) WHERE ("lft" > 643)
AREL (0.4ms)  UPDATE "comments" SET "rgt" = ("rgt" - 2) WHERE ("rgt" > 643)
AREL (0.2ms)  DELETE FROM "comments" WHERE ("comments"."id" = 377)
AREL (0.7ms)  DELETE FROM "comments" WHERE ("comments"."lft" > 641 AND "comments"."rgt" < 652)
AREL (0.9ms)  UPDATE "comments" SET "lft" = ("lft" - 12) WHERE ("lft" > 652)
AREL (0.9ms)  UPDATE "comments" SET "rgt" = ("rgt" - 12) WHERE ("rgt" > 652)
AREL (0.3ms)  DELETE FROM "comments" WHERE ("comments"."id" = 376)
AREL (0.4ms)  DELETE FROM "threads" WHERE ("threads"."id" = 101)
AREL (0.4ms)  DELETE FROM "groups" WHERE ("groups"."id" = 57)

Is this normal behavior for acts as nested? I would have expect just the DELETE FROM COMMENTS where Comment.id = XXXX. But instead all this is going on and comment records are breaking.

Has anyone seen this before?

Thank you

UPDATE w What is being used to prevent deep nesting:

  after_save :ensure_max_nestedset_level
  def ensure_max_nestedset_level
    if self.level > 2
      self.move_to_child_of(parent.parent)
    end
  end
AnApprentice
  • 108,152
  • 195
  • 629
  • 1,012
  • Delete problem with awesome_nested_set. May be you can help me http://stackoverflow.com/questions/5976085/deletion-of-a-parent-node-but-not-the-children-with-awesome-nested-set – krunal shah May 13 '11 at 04:49

4 Answers4

3

This should not be breaking records. When you delete a node, the tree has to be pruned. The comments in acts_as_nested_set say that

"both adding and removing an entry require a full table write."

The code in this before destroy method in acts_as_nested_set is attempting reorganize the table when you delete the Comment records, and is normal behavior.

Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
  • What Rails gem should I be using? Maybe that's the issue? gem XXX ? – AnApprentice Feb 25 '11 at 02:39
  • 1
    Perhaps. I'd look at [`awesome_nested_set`](https://github.com/collectiveidea/awesome_nested_set). Collective Idea has some great gems. It has been a while since it's been updated, though, so YMMV. See also [this SO question/answer](http://stackoverflow.com/questions/4308787/using-rails-polymorphism-for-nested-comments/4308919#4308919). – Michelle Tilley Feb 25 '11 at 02:46
  • nice reference post, perfectly helpful. – AnApprentice Feb 26 '11 at 18:04
  • I see from another question you're trying out `awesome_nested_set`. [`nested_set`](https://github.com/skyeagle/nested_set) also appears to be Rails 3 ready, if you want to try another (it's forked from awesome_nested_set as well, but is a gem). – Michelle Tilley Feb 26 '11 at 18:15
  • Another interesting gem to try, which doesn't use the nested set model, but appears to be pretty fast (according to [reports](http://www.reddit.com/r/ruby/comments/dvlxa/rails_3_compatible_ancestry_122_gem_released/c13ajrr)): [Ancestry](https://github.com/stefankroes/ancestry) – Michelle Tilley Feb 26 '11 at 18:43
  • Also, since we've turned this into a discussion thread :P you may even want to consider trying out a [document-oriented database](http://www.mongodb.org/), and store your threaded discussion inline. – Michelle Tilley Feb 26 '11 at 18:45
  • "I see from another question you're trying out awesome_nested_set" Right, as covered in my answer above, which explained (first) that this is normal behavior. – TK-421 Feb 26 '11 at 19:57
  • how can I QA that when deleting nested items, it's not going to break other non-related items which is what was happening? – AnApprentice Feb 28 '11 at 23:31
  • Also, I want to prevent nesting going to deep, so I added this in my model, can that be the problem ? (Q UPDATED) – AnApprentice Feb 28 '11 at 23:32
  • (1) I would add tests to your test suite that builds a few nested items, deletes a particular entry, and then checks to see if the rest of the entries are broken or not. (2) Which gem is this with? `acts_as_nested_set` doesn't have this method as far as I can tell. However, `awesome_nested_set` and friends recalculate the tree when you call that method, so I would *think* that it should be safe, if perhaps a bit slow (a better solution may be to check *before* save if the nesting will be too deep; currently you're calculating twice if the level is `>` 2). – Michelle Tilley Feb 28 '11 at 23:56
  • Here is a list of all similar gems and their recent activity: https://www.ruby-toolbox.com/categories/Active_Record_Nesting – Chloe Apr 02 '14 at 23:30
2

Well it's apparently trying to maintain the hierarchy when deleting nodes, a common problem in SQL.

Which library are you using? Awesome Nested Set? Why it would result in broken records I'm not sure.

You can read more about the nested set approach here:

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

See the "Nested Set Model" section, and the "Deleted Nodes" sub-section below for why deletions are managed the way they are.

So the delete statements would be normal behavior.

TK-421
  • 10,598
  • 3
  • 38
  • 34
1

It seems unlikely, but never impossible, that the gem you use is unable to delete items and keep the nestedness correct. So imho it is related to something you are doing. It might also be related to the polymorphic relation.

Did you test with the ensure_max_nestedset_level removed? Does it work then? Does it work when you delete a single comment? Delete a parent-comment? (with nested elements under it). Does it only fail when you delete a group/thread?

nathanvda
  • 49,707
  • 13
  • 117
  • 139
1

Simple acts_as_nested_set creates a tree for all records in the comments table. I guess you should create one comment tree per thread. E.g. acts_as_nested_set :parent_column => :parent_id, :scope => [:commentable_id, :commentable_type].

Notice also that by default associations created by acts_as_nested_set have :dependent set to :delete_all. So make sure that :parent_id is set correctly.

With above modifications when deleting a group, only comments which belong to the group (via threads) should be deleted.

BTW I use awesome_nested_set recommended at ruby-toolbox.

Greg Dan
  • 6,198
  • 3
  • 33
  • 53