class HumanQL::TreeNormalizer

Normalizes and imposes various limitations on query abstract syntax trees (ASTs).

Constants

EMPTY_STACK

Attributes

nested_not[RW]

Allow nested :not (in other words, double negatives)? Default: false -> nested :not nodes are removed.

nested_scope[RW]

Allow nested scopes? Default: false -> nested scope nodes are removed.

not_scope[RW]

Allow SCOPE within :not?

  • If set to `:invert` normalizes `[:not, ['SCOPE', 'a']]` to `['SCOPE', [:not, 'a']]`.

  • If set to `false`, the nested scope node is removed.

  • For either :invert or false, the scope node is otherwise removed if found below a :not node.

Default: :invert

scope_and_only[RW]

Allow only scopes combined with :and condition? Default: false

scope_at_top_only[RW]

Allow scope at root or first level only? Default: false

scope_can_constrain[RW]

Does a scope count as a constraint? Default: true -> a scope is a constraint if its argument is a constraint. If it depends on the scope, you can override scope_can_constrain? with this logic.

unconstrained_not[RW]

Allow unconstrained :not? Queries containing an unconstrained :not may be costly to execute. If false the unconstrained :not will be removed.

A :not node is considered “constrained” if it has an :and ancestor with at least one constraint argument. A constraint argument is a term, phrase, or :and node matching this same criteria, or an :or node where all arguments match this criteria. See also scope_can_constrain. Default: true

Public Class Methods

new(opts = {}) click to toggle source

Construct given options that are applied via same name setters on self.

# File lib/human-ql/tree_normalizer.rb, line 69
def initialize(opts = {})
  @nested_scope = false
  @nested_not = false
  @unconstrained_not = true
  @scope_can_constrain = true
  @scope_at_top_only = false
  @scope_and_only = false
  @not_scope = :invert

  opts.each do |name,val|
    send(name.to_s + '=', val)
  end
end

Public Instance Methods

normalize(node) click to toggle source

Return a new normalized AST from the given AST root node.

# File lib/human-ql/tree_normalizer.rb, line 84
def normalize(node)
  node = normalize_1(node, EMPTY_STACK, @unconstrained_not)
  if (@not_scope != true) || @scope_and_only || @scope_at_top_only
    node = normalize_2(node, EMPTY_STACK)
  end
  node
end

Protected Instance Methods

constraint?(node) click to toggle source

Return true if node is a valid constraint

# File lib/human-ql/tree_normalizer.rb, line 101
def constraint?(node)
  op,*args = node
  if ! node.is_a?(Array)
    true
  elsif args.empty?
    false
  else
    case op
    when :and
      args.any? { |a| constraint?(a) }
    when :or
      args.all? { |a| constraint?(a) }
    when :phrase
      true
    when String
      scope_can_constrain?(op) && constraint?(args.first)
    else
      false
    end
  end
end
scope_can_constrain?(scope) click to toggle source
# File lib/human-ql/tree_normalizer.rb, line 94
def scope_can_constrain?(scope)
  @scope_can_constrain
end