<commit_before>module DataTables
  module Modules
    class Search

      attr_reader :scope, :context

      def initialize(model, scope, params)
        @scope = scope.dup
        @model = model
        @params = params
      end

      def search
        columns = searchable_columns(@params.dig(:search, :value), @params[:columns])

        searches = DataTables::Responder.flat_keys_to_nested columns

        search_by, join_hash = build_search(@model, searches)

        @scope = @scope.joins(join_hash)

        @scope.where(search_by.reduce(:and))
      end

      def build_search(model, in_hash, join_hash = nil)
        # Tuple!
        return in_hash.inject([]) { |queries, (column, query)|
          case query
          when Hash
            if (assoc = model.reflect_on_association(column))
              new_queries, join_class = build_search(assoc.klass, query)

              join_hash = join_class.nil? ? [column] : {column => join_class}

              queries << new_queries.reduce(:and)
            else
              warn("trying to reflect on #{column} but #{model.class.name} has no such association.")
            end
          else
            search_by_type(model, column, query) do |result|
              queries << result
            end
          end
          queries
        }, join_hash
      end

      protected

      def search_by_type(model, column, query, &block)
        result = case model.columns_hash[column.to_s]&.type
        when :string
          # I'm pretty sure this is safe from SQL Injection
          model.arel_table[column].matches("%#{query}%")
        when :integer
          model.arel_table[column].eq(value) if value = query&.to_i
        when :datetime
          datetime = Time.parse(query)
          range = (datetime-1.second)..(datetime+1.second)
          model.arel_table[column].between(range)
        end<commit_msg>Fix search module to work with integers<commit_after>module DataTables
  module Modules
    class Search

      attr_reader :scope, :context

      def initialize(model, scope, params)
        @scope = scope.dup
        @model = model
        @params = params
      end

      def search
        columns = searchable_columns(@params.dig(:search, :value), @params[:columns])

        searches = DataTables::Responder.flat_keys_to_nested columns

        search_by, join_hash = build_search(@model, searches)

        @scope = @scope.joins(join_hash)

        @scope.where(search_by.reduce(:and))
      end

      def build_search(model, in_hash, join_hash = nil)
        # Tuple!
        return in_hash.inject([]) { |queries, (column, query)|
          case query
          when Hash
            if (assoc = model.reflect_on_association(column))
              new_queries, join_class = build_search(assoc.klass, query)

              join_hash = join_class.nil? ? [column] : {column => join_class}

              queries << new_queries.reduce(:and)
            else
              warn("trying to reflect on #{column} but #{model.class.name} has no such association.")
            end
          else
            search_by_type(model, column, query) do |result|
              queries << result
            end
          end
          queries
        }, join_hash
      end

      protected

      def search_by_type(model, column, query, &block)
        result = case model.columns_hash[column.to_s]&.type
        when :string
          # I'm pretty sure this is safe from SQL Injection
          model.arel_table[column].matches("%#{query}%")
        when :integer
          value = query&.to_i and model.arel_table[column].eq(value)
        when :datetime
          datetime = Time.parse(query)
          range = (datetime-1.second)..(datetime+1.second)
          model.arel_table[column].between(range)
        end
{'fraction_non_alphanumeric': 0.09892133649039726, 'fraction_numerical': 0.0010523546435148646, 'mean_word_length': 2.048917401764234, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 5, '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': '1862756', 'n_tokens_mistral': 1082, 'n_tokens_neox': 1058, 'n_words': 259}