<commit_before>class OptionNode < ActiveRecord::Base
  include MissionBased, FormVersionable, Replication::Replicable

  # Number of descendants that make a 'huge' node.
  HUGE_CUTOFF = 100

  # Number of nodes to return as JSON if node is 'huge'.
  TO_SERIALIZE_IF_HUGE = 10

  belongs_to :option_set
  belongs_to :option, autosave: true
  has_ancestry cache_depth: true

  before_validation { self.ancestry = nil if self.ancestry.blank? }
  before_destroy :ensure_no_answers_or_choices
  after_save :update_children

  attr_accessor :children_attribs
  attr_reader :option_attribs
  alias_method :c, :children

  # This attribute is set ONLY after an update using children_attribs.
  # It is true only if the node or any of its descendants have existing children
  # and the update causes their ranks to change.
  attr_accessor :ranks_changed, :options_added, :options_removed
  attr_writer :child_options
  alias_method :ranks_changed?, :ranks_changed
  alias_method :options_added?, :options_added
  alias_method :options_removed?, :options_removed

  replicable child_assocs: [:children, :option], backward_assocs: :option_set, dont_copy: [:option_set_id, :option_id]

  # Given a set of nodes, preloads child_options for all in constant number of queries.
  def self.preload_child_options(roots)
    ancestries = roots.map{ |r| "'#{r.id}'" }.join(',')
    nodes_by_root_id = OptionNode.includes(:option).where("ancestry IN (#{ancestries})").order('rank').group_by{ |n| n.ancestry.to_i }
    roots.each{ |r| r.child_options = (nodes_by_root_id[r.id] || []).map(&:option) }
  end

  # Efficiently gets an option id from an option node id. id may be a string or integer.
  def self.id_to_option_id(id)
    where(id: id).pluck(:option_id).first
  end

  # Overriding this to avoid error from ancestry.
  alias_method :_children, :children
  def children
    new_record? ? [] : _children
  end

  def has_grandchildren?
    @has_grandchildren ||= descendants(at_depth: 2).any?
  end

  def all_options
    Option.where(id: descendants.map(&:option_id))
  end

  def max_depth
    @max_depth ||= descendants.maximum('ancestry_depth')
  end

  # Returns options of children, ordered by rank.
  def child_options
    @child_options ||= sorted_children.includes(:option).map(&:option)
  end

  # Returns the child options of the node defined by path of option ids.
  # If node at end of path is leaf node, returns [].
  def options_for_node(path)
    x = find_descendant_by_option_path(path)
    find_descendant_by_option_path(path).try(:child_options) || []
  end

  # Traces the given path of option ids down the tree, returning the OptionNode at the end.
  # Assumes path is an array of Option IDs with 0 or more elements.
  # Returns self if path is empty.
  # Returns nil if any point in path does not find a match.
  # Returns nil if path contains any nils.
  def find_descendant_by_option_path(path)
    return self if path.empty?
    return nil if path.any?(&:nil?)
    return nil unless match = child_with_option_id(path[0])
    match.find_descendant_by_option_path(path[1..-1])
  end

  def child_with_option_id(oid)
    children.detect{ |c| c.option_id == oid }
  end

  # The total number of descendant options.
  def total_options
    @total_options ||= descendants.count
  end<commit_msg>285: Remove extra call to find_descendant_by_option_path<commit_after>class OptionNode < ActiveRecord::Base
  include MissionBased, FormVersionable, Replication::Replicable

  # Number of descendants that make a 'huge' node.
  HUGE_CUTOFF = 100

  # Number of nodes to return as JSON if node is 'huge'.
  TO_SERIALIZE_IF_HUGE = 10

  belongs_to :option_set
  belongs_to :option, autosave: true
  has_ancestry cache_depth: true

  before_validation { self.ancestry = nil if self.ancestry.blank? }
  before_destroy :ensure_no_answers_or_choices
  after_save :update_children

  attr_accessor :children_attribs
  attr_reader :option_attribs
  alias_method :c, :children

  # This attribute is set ONLY after an update using children_attribs.
  # It is true only if the node or any of its descendants have existing children
  # and the update causes their ranks to change.
  attr_accessor :ranks_changed, :options_added, :options_removed
  attr_writer :child_options
  alias_method :ranks_changed?, :ranks_changed
  alias_method :options_added?, :options_added
  alias_method :options_removed?, :options_removed

  replicable child_assocs: [:children, :option], backward_assocs: :option_set, dont_copy: [:option_set_id, :option_id]

  # Given a set of nodes, preloads child_options for all in constant number of queries.
  def self.preload_child_options(roots)
    ancestries = roots.map{ |r| "'#{r.id}'" }.join(',')
    nodes_by_root_id = OptionNode.includes(:option).where("ancestry IN (#{ancestries})").order('rank').group_by{ |n| n.ancestry.to_i }
    roots.each{ |r| r.child_options = (nodes_by_root_id[r.id] || []).map(&:option) }
  end

  # Efficiently gets an option id from an option node id. id may be a string or integer.
  def self.id_to_option_id(id)
    where(id: id).pluck(:option_id).first
  end

  # Overriding this to avoid error from ancestry.
  alias_method :_children, :children
  def children
    new_record? ? [] : _children
  end

  def has_grandchildren?
    @has_grandchildren ||= descendants(at_depth: 2).any?
  end

  def all_options
    Option.where(id: descendants.map(&:option_id))
  end

  def max_depth
    @max_depth ||= descendants.maximum('ancestry_depth')
  end

  # Returns options of children, ordered by rank.
  def child_options
    @child_options ||= sorted_children.includes(:option).map(&:option)
  end

  # Returns the child options of the node defined by path of option ids.
  # If node at end of path is leaf node, returns [].
  def options_for_node(path)
    find_descendant_by_option_path(path).try(:child_options) || []
  end

  # Traces the given path of option ids down the tree, returning the OptionNode at the end.
  # Assumes path is an array of Option IDs with 0 or more elements.
  # Returns self if path is empty.
  # Returns nil if any point in path does not find a match.
  # Returns nil if path contains any nils.
  def find_descendant_by_option_path(path)
    return self if path.empty?
    return nil if path.any?(&:nil?)
    return nil unless match = child_with_option_id(path[0])
    match.find_descendant_by_option_path(path[1..-1])
  end

  def child_with_option_id(oid)
    children.detect{ |c| c.option_id == oid }
  end

  # The total number of descendant options.
  def total_options
    @total_options ||= descendants.count
  end
{'fraction_non_alphanumeric': 0.08884172225591268, 'fraction_numerical': 0.0034869617950272895, 'mean_word_length': 4.281825460368295, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1275793', 'n_tokens_mistral': 2205, 'n_tokens_neox': 2122, 'n_words': 658}