<commit_before>=begin rdoc
=Description
Name Space that contains the classes and modules used to build search quries. Its primary usage is done though
the Query class. The query class builds sql condition arrays for active record finds.
=Usage
Usage typically consists of a combination of active records with scope or find and initialize
and conditions.  Here is an example of using it with a find and a with scope
  Patient.find(:all, :conditions => Search::Query.new("search_string").conditions) # => Uses find and a simple search string

  # This wraps the search conditions with an ORDER BY statement
  Patient.with_scope(:find => {:order => "patients.modified_date DESC"}) do
    find(:all, :conditions => Search::Query.new(search_string).conditions(:exam_description => lambda { query.term_to_name_string(:exam_description) }) )
  end
=end
module Search

  class Query
    attr_accessor :search_string, :terms, :operator

    # Initializes the Query class with a search term that can be a string or a Hash containing field value pairs
    def initialize(string_or_hash)
      if string_or_hash.class == String
        self.search_string = string_or_hash.strip
        self.operator = "OR"
      elsif string_or_hash.class == Hash
        self.terms = string_or_hash.keys.inject({}) {|hash,key| hash[key] = string_or_hash[key].strip; hash }
        self.operator = "AND"
      end
    end

    # Takes the search term and builds additional terms based on number rules.
    # Currently this is only removing leading zeros and then ensuring that the list
    # of search terms returned contains unique entries
    def term_to_number(term)
      self.search_string ? sstring = self.search_string : sstring = terms[term]
      if not sstring.blank?
        mrns = []
        mrns << sstring.gsub(/^0+/,"") unless sstring.match(/^0+$/)
        mrns << sstring
        mrns.uniq
      else
        []
      end
    end

    # Takes the search term and builds additional terms based on name rules.
    # Currently these rules build permutations of the name spliting on the space
    # character, the carrot (^), and the comma (,).  It creates a list of all these
    # permutations using the +Search::Helpers.permutations+ method.
    def term_to_name_string(term)
      self.search_string ? sstring = self.search_string : sstring = terms[term]
      if not sstring.blank?
        names = sstring.split(/, +|[,\^\- ]/).collect do |e|
          Regexp.escape(e)
        end
        names.uniq
      else
        []
      end
    end

    # Builds an array that can be passed to active record for sanitation, e.g., ["sql_field = ?","subsitution value"]
    def conditions(method_mapping=nil)
      unless method_mapping
        method_mapping = {<commit_msg>Add regex escaping to mrn search<commit_after>=begin rdoc
=Description
Name Space that contains the classes and modules used to build search quries. Its primary usage is done though
the Query class. The query class builds sql condition arrays for active record finds.
=Usage
Usage typically consists of a combination of active records with scope or find and initialize
and conditions.  Here is an example of using it with a find and a with scope
  Patient.find(:all, :conditions => Search::Query.new("search_string").conditions) # => Uses find and a simple search string

  # This wraps the search conditions with an ORDER BY statement
  Patient.with_scope(:find => {:order => "patients.modified_date DESC"}) do
    find(:all, :conditions => Search::Query.new(search_string).conditions(:exam_description => lambda { query.term_to_name_string(:exam_description) }) )
  end
=end
module Search

  class Query
    attr_accessor :search_string, :terms, :operator

    # Initializes the Query class with a search term that can be a string or a Hash containing field value pairs
    def initialize(string_or_hash)
      if string_or_hash.class == String
        self.search_string = string_or_hash.strip
        self.operator = "OR"
      elsif string_or_hash.class == Hash
        self.terms = string_or_hash.keys.inject({}) {|hash,key| hash[key] = string_or_hash[key].strip; hash }
        self.operator = "AND"
      end
    end

    # Takes the search term and builds additional terms based on number rules.
    # Currently this is only removing leading zeros and then ensuring that the list
    # of search terms returned contains unique entries
    def term_to_number(term)
      self.search_string ? sstring = self.search_string : sstring = terms[term]
      if not sstring.blank?
        mrns = []
        mrns << sstring.gsub(/^0+/,"") unless sstring.match(/^0+$/)
        mrns << sstring
        mrns.uniq.collect do |e|
          Regexp.escape(e)
        end
      else
        []
      end
    end

    # Takes the search term and builds additional terms based on name rules.
    # Currently these rules build permutations of the name spliting on the space
    # character, the carrot (^), and the comma (,).  It creates a list of all these
    # permutations using the +Search::Helpers.permutations+ method.
    def term_to_name_string(term)
      self.search_string ? sstring = self.search_string : sstring = terms[term]
      if not sstring.blank?
        names = sstring.split(/, +|[,\^\- ]/).collect do |e|
          Regexp.escape(e)
        end
        names.uniq
      else
        []
      end
    end

    # Builds an array that can be passed to active record for sanitation, e.g., ["sql_field = ?","subsitution value"]
    def conditions(method_mapping=nil)
      unless method_mapping
        method_mapping = {
{'fraction_non_alphanumeric': 0.08493101594696291, 'fraction_numerical': 0.0007167174341515857, 'mean_word_length': 3.0744525547445254, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, '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': '3907127', 'n_tokens_mistral': 1585, 'n_tokens_neox': 1520, 'n_words': 583}