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

Returns:

  • (undefined)

# 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

Returns:

  • (undefined)

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

Instance Method Details

#ancestor_of?(other) ⇒ Boolean

Is this document an ancestor of the other document?

Parameters:

Returns:

  • (Boolean)

    The document is 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

Returns:

  • (Mongoid::Criteria)

    Mongoid criteria to retrieve the documents 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

Returns:

  • (Array<Mongoid::Document>)

    Array of the 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.

Returns:

  • (Mongoid::Criteria)

    Mongoid criteria to retrieve the document's children


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

#delete_descendantsundefined

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

Returns:

  • (undefined)

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

Returns:

  • (Fixnum)

    Depth of this document


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?

Parameters:

Returns:

  • (Boolean)

    The document is 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

Returns:

  • (Mongoid::Criteria)

    Mongoid criteria to retrieve the 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

Returns:

  • (Array<Mongoid::Document>)

    Array of the document itself 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)

Returns:

  • (undefined)

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

Returns:

  • (Boolean)

    Whether the document is a leaf node


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)

Returns:

  • (Mongoid::Criteria)

    Mongoid criteria to retrieve the document's leaves


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

Returns:

  • (undefined)

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

Returns:

  • (undefined)

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.

Returns:

  • (Mongoid::Document)

    The document's parent document


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

#parent=(document) ⇒ Object

Note:

Generated by Mongoid

Sets this documents parent document.

Parameters:


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

Returns:

  • (Array<BSON::ObjectId>)

    The ids of the document's ancestors


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

#rearrange_children!undefined

Forces rearranging of all children after next save

Returns:

  • (undefined)

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?

Returns:

  • (Boolean)

    Whether the children will be rearranged


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

Returns:

  • (Mongoid::Document)

    The documents root node


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

Returns:

  • (Boolean)

    Whether the document is a root node


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?

Parameters:

Returns:

  • (Boolean)

    The document is 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

Returns:

  • (Mongoid::Criteria)

    Mongoid criteria to retrieve the 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

Returns:

  • (Mongoid::Criteria)

    Mongoid criteria to retrieve the 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