Module: Mongoid::Tree

Extended by:
ActiveSupport::Concern
Defined in:
lib/mongoid/tree.rb,
lib/mongoid/tree/ordering.rb,
lib/mongoid/tree/traversal.rb

Overview

Mongoid::Tree

This module extends any Mongoid document with tree functionality.

Usage

Simply include the module in any Mongoid document:

class Node
  include Mongoid::Document
  include Mongoid::Tree
end

Using the tree structure

Each document references many children. You can access them using the #children method.

node = Node.create
node.children.create
node.children.count # => 1

Every document references one parent (unless it's a root document).

node = Node.create
node.parent # => nil
node.children.create
node.children.first.parent # => node

Destroying

Mongoid::Tree does not handle destroying of nodes by default. However it provides several strategies that help you to deal with children of deleted documents. You can simply add them as before_destroy callbacks.

Available strategies are:

  • :nullify_children – Sets the children's parent_id to null

  • :move_children_to_parent – Moves the children to the current document's parent

  • :destroy_children – Destroys all children by calling their #destroy method (invokes callbacks)

  • :delete_descendants – Deletes all descendants using a database query (doesn't invoke callbacks)

Example:

class Node
  include Mongoid::Document
  include Mongoid::Tree

  before_destroy :nullify_children
end

Callbacks

Mongoid::Tree offers callbacks for its rearranging process. This enables you to rebuild certain fields when the document was moved in the tree. Rearranging happens before the document is validated. This gives you a chance to validate your additional changes done in your callbacks. See ActiveModel::Callbacks and ActiveSupport::Callbacks for further details on callbacks.

Example:

class Page
  include Mongoid::Document
  include Mongoid::Tree

  after_rearrange :rebuild_path

  field :slug
  field :path

  private

  def rebuild_path
    self.path = self.ancestors_and_self.collect(&:slug).join('/')
  end
end

Defined Under Namespace

Modules: ClassMethods, Ordering, Traversal

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.after_rearrangeundefined

Note:

Generated by ActiveSupport

Sets a callback that is called after the document is rearranged

Examples:

class Node
  include Mongoid::Document
  include Mongoid::Tree

  after_rearrange :do_something

private

  def do_something
    # ...
  end
end

# File 'lib/mongoid/tree.rb', line 184


.before_rearrangeundefined

Note:

Generated by ActiveSupport

Sets a callback that is called before the document is rearranged

Examples:

class Node
  include Mongoid::Document
  include Mongoid::Tree

  before_rearrage :do_something

private

  def do_something
    # ...
  end
end

# File 'lib/mongoid/tree.rb', line 160


Instance Method Details

#ancestor_of?(other) ⇒ Boolean

Is this document an ancestor of the other document?


307
308
309
# File 'lib/mongoid/tree.rb', line 307

def ancestor_of?(other)
  other.parent_ids.include?(self.id)
end

#ancestorsMongoid::Criteria

Returns a chainable criteria for this document's ancestors


289
290
291
# File 'lib/mongoid/tree.rb', line 289

def ancestors
  base_class.where(:_id.in => parent_ids).order(:depth => :asc)
end

#ancestors_and_selfArray<Mongoid::Document>

Returns an array of this document's ancestors and itself


297
298
299
# File 'lib/mongoid/tree.rb', line 297

def ancestors_and_self
  ancestors + [self]
end

#childrenMongoid::Criteria

Note:

Generated by Mongoid

Returns a list of the document's children. It's a references_many association.


# File 'lib/mongoid/tree.rb', line 208


#delete_descendantsundefined

Deletes all descendants using the database (doesn't invoke callbacks)


413
414
415
# File 'lib/mongoid/tree.rb', line 413

def delete_descendants
  base_class.delete_all(:conditions => { :parent_ids => self.id })
end

#depthFixnum

Returns the depth of this document (number of ancestors)

Examples:

Node.root.depth # => 0
Node.root.children.first.depth # => 1

248
249
250
# File 'lib/mongoid/tree.rb', line 248

def depth
  super || parent_ids.count
end

#descendant_of?(other) ⇒ Boolean

Is this document a descendant of the other document?


333
334
335
# File 'lib/mongoid/tree.rb', line 333

def descendant_of?(other)
  self.parent_ids.include?(other.id)
end

#descendantsMongoid::Criteria

Returns a chainable criteria for this document's descendants


315
316
317
# File 'lib/mongoid/tree.rb', line 315

def descendants
  base_class.where(:parent_ids => self.id)
end

#descendants_and_selfArray<Mongoid::Document>

Returns and array of this document and it's descendants


323
324
325
# File 'lib/mongoid/tree.rb', line 323

def descendants_and_self
  [self] + descendants
end

#destroy_childrenundefined

Destroys all children by calling their #destroy method (does invoke callbacks)


421
422
423
# File 'lib/mongoid/tree.rb', line 421

def destroy_children
  children.destroy_all
end

#leaf?Boolean

Is this document a leaf node (has no children)?


264
265
266
# File 'lib/mongoid/tree.rb', line 264

def leaf?
  children.empty?
end

#leavesMongoid::Criteria

Returns all leaves of this document (be careful, currently involves two queries)


367
368
369
# File 'lib/mongoid/tree.rb', line 367

def leaves
  base_class.where(:_id.nin => base_class.only(:parent_id).collect(&:parent_id)).and(:parent_ids => self.id)
end

#move_children_to_parentundefined

Moves all children to this document's parent


402
403
404
405
406
407
# File 'lib/mongoid/tree.rb', line 402

def move_children_to_parent
  children.each do |c|
    c.parent = self.parent
    c.save
  end
end

#nullify_childrenundefined

Nullifies all children's parent_id


391
392
393
394
395
396
# File 'lib/mongoid/tree.rb', line 391

def nullify_children
  children.each do |c|
    c.parent = c.parent_id = nil
    c.save
  end
end

#parentMongoid::Document

Note:

Generated by Mongoid

Returns the document's parent (unless it's a root document). It's a referenced_in association.


# File 'lib/mongoid/tree.rb', line 216


#parent=(document) ⇒ Object

Note:

Generated by Mongoid

Sets this documents parent document.


# File 'lib/mongoid/tree.rb', line 224


#parent_idsArray<BSON::ObjectId>

Note:

Generated by Mongoid

Returns a list of the document's parent_ids, starting with the root node.


# File 'lib/mongoid/tree.rb', line 232


#rearrange_children!undefined

Forces rearranging of all children after next save


375
376
377
# File 'lib/mongoid/tree.rb', line 375

def rearrange_children!
  @rearrange_children = true
end

#rearrange_children?Boolean

Will the children be rearranged after next save?


383
384
385
# File 'lib/mongoid/tree.rb', line 383

def rearrange_children?
  !!@rearrange_children
end

#rootMongoid::Document

Returns this document's root node. Returns `self` if the current document is a root node

Examples:

node = Node.find(...)
node.root

277
278
279
280
281
282
283
# File 'lib/mongoid/tree.rb', line 277

def root
  if parent_ids.present?
    base_class.find(parent_ids.first)
  else
    self.root? ? self : self.parent.root
  end
end

#root?Boolean

Is this document a root node (has no parent)?


256
257
258
# File 'lib/mongoid/tree.rb', line 256

def root?
  parent_id.nil?
end

#sibling_of?(other) ⇒ Boolean

Is this document a sibling of the other document?


359
360
361
# File 'lib/mongoid/tree.rb', line 359

def sibling_of?(other)
  self.parent_id == other.parent_id
end

#siblingsMongoid::Criteria

Returns this document's siblings


341
342
343
# File 'lib/mongoid/tree.rb', line 341

def siblings
  siblings_and_self.excludes(:id => self.id)
end

#siblings_and_selfMongoid::Criteria

Returns this document's siblings and itself


349
350
351
# File 'lib/mongoid/tree.rb', line 349

def siblings_and_self
  base_class.where(:parent_id => self.parent_id)
end