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

Instance Method Summary collapse

Instance Method Details

#ancestor_of?(other) ⇒ Boolean

Is this document an ancestor of the other document?

Returns:

  • (Boolean)


210
211
212
# File 'lib/mongoid/tree.rb', line 210

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

#ancestorsObject

Returns a chainable criteria for this document’s ancestors



198
199
200
# File 'lib/mongoid/tree.rb', line 198

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

#ancestors_and_selfObject

Returns an array of this document’s ancestors and itself



204
205
206
# File 'lib/mongoid/tree.rb', line 204

def ancestors_and_self
  ancestors + [self]
end

#delete_descendantsObject

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



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

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

#depthObject

Returns the depth of this document (number of ancestors)



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

def depth
  parent_ids.count
end

#descendant_of?(other) ⇒ Boolean

Is this document a descendant of the other document?

Returns:

  • (Boolean)


228
229
230
# File 'lib/mongoid/tree.rb', line 228

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

#descendantsObject

Returns a chainable criteria for this document’s descendants



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

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

#descendants_and_selfObject

Returns and array of this document’s descendants and itself



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

def descendants_and_self
  [self] + descendants
end

#destroy_childrenObject

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



294
295
296
# File 'lib/mongoid/tree.rb', line 294

def destroy_children
  children.destroy_all
end

#leaf?Boolean

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

Returns:

  • (Boolean)


176
177
178
# File 'lib/mongoid/tree.rb', line 176

def leaf?
  children.empty?
end

#leavesObject

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



252
253
254
# File 'lib/mongoid/tree.rb', line 252

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

#move_children_to_parentObject

Moves all children to this document’s parent



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

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

#nullify_childrenObject

Nullifies all children’s parent_id



270
271
272
273
274
275
# File 'lib/mongoid/tree.rb', line 270

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

#rearrange_children!Object

Forces rearranging of all children after next save



258
259
260
# File 'lib/mongoid/tree.rb', line 258

def rearrange_children!
  @rearrange_children = true
end

#rearrange_children?Boolean

Will the children be rearranged after next save?

Returns:

  • (Boolean)


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

def rearrange_children?
  !!@rearrange_children
end

#rootObject

Returns this document’s root node



188
189
190
191
192
193
194
# File 'lib/mongoid/tree.rb', line 188

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

#root?Boolean

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

Returns:

  • (Boolean)


170
171
172
# File 'lib/mongoid/tree.rb', line 170

def root?
  parent_id.nil?
end

#sibling_of?(other) ⇒ Boolean

Is this document a sibling of the other document?

Returns:

  • (Boolean)


246
247
248
# File 'lib/mongoid/tree.rb', line 246

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

#siblingsObject

Returns this document’s siblings



234
235
236
# File 'lib/mongoid/tree.rb', line 234

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

#siblings_and_selfObject

Returns this document’s siblings and itself



240
241
242
# File 'lib/mongoid/tree.rb', line 240

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