<commit_before>module Mobility
  module Backends
    module ActiveRecord
=begin

Defines query methods for Postgres backends. Including class must define a
single method, +contains_value+, which accepts a column and value to match, and
returns an Arel node.

This module avoids 99% duplication between hstore and jsonb backend querying
code.

@see Mobility::Backends::ActiveRecord::Jsonb::QueryMethods
@see Mobility::Backends::ActiveRecord::Hstore::QueryMethods

=end
      module PgQueryMethods
        def initialize(attributes, _)
          super
          q = self

          define_method :where! do |opts, *rest|
            if i18n_keys = q.extract_attributes(opts)
              opts = opts.with_indifferent_access
              query = q.create_where_query!(opts, i18n_keys, arel_table)

              opts.empty? ? super(query) : super(opts, *rest).where(query)
            else
              super(opts, *rest)
            end
          end
        end

        def extended(relation)
          super
          q = self
          m = relation.model.arel_table

          mod = Module.new do
            define_method :not do |opts, *rest|
              if i18n_keys = q.extract_attributes(opts)
                opts = opts.with_indifferent_access
                query = q.create_not_query!(opts, i18n_keys, m)

                super(opts, *rest).where(query)
              else
                super(opts, *rest)
              end
            end
          end
          relation.mobility_where_chain.include(mod)
        end

        # Create +where+ query for options hash, translated keys and arel_table
        # @note This is a destructive operation, it will modify +opts+.
        #
        # @param [Hash] opts Hash of attribute/value pairs
        # @param [Array] keys Translated attribute names
        # @param [Arel::Table] arel_table Model or relation's arel table
        # @return [Arel::Node] Arel node to pass to +where+
        def create_where_query!(opts, keys, arel_table)
          keys.map { |key|
            column = arel_table[key.to_sym]
            value = opts.delete(key)

            value.nil? ?
              has_locale(column).not :
              contains_value(column, value)
          }.inject(&:and)
        end

        # Create +not+ query for options hash, translated keys and arel_table
        # @note This is a destructive operation, it will modify +opts+.
        #
        # @param [Hash] opts Hash of attribute/value pairs
        # @param [Array] keys Translated attribute names
        # @param [Arel::Table] arel_table Model or relation's arel table
        # @return [Arel::Node] Arel node to pass to +where+
        def create_not_query!(opts, keys, arel_table)
          keys.map { |key|
            column = arel_table[key.to_sym]
            has_locale(column).
              and(contains_value(column, opts.delete(key)).not)
          }.inject(&:and)
        end

        private

        def contains_value(_column, _value)
          raise NotImplementedError
        end

        def has_locale(column)
          build_infix(:'?', column, quoted_locale)<commit_msg>Handle array of values in jsonb/hstore query methods<commit_after>module Mobility
  module Backends
    module ActiveRecord
=begin

Defines query methods for Postgres backends. Including class must define a
single method, +contains_value+, which accepts a column and value to match, and
returns an Arel node.

This module avoids 99% duplication between hstore and jsonb backend querying
code.

@see Mobility::Backends::ActiveRecord::Jsonb::QueryMethods
@see Mobility::Backends::ActiveRecord::Hstore::QueryMethods

=end
      module PgQueryMethods
        def initialize(attributes, _)
          super
          q = self

          define_method :where! do |opts, *rest|
            if i18n_keys = q.extract_attributes(opts)
              opts = opts.with_indifferent_access
              query = q.create_where_query!(opts, i18n_keys, arel_table)

              opts.empty? ? super(query) : super(opts, *rest).where(query)
            else
              super(opts, *rest)
            end
          end
        end

        def extended(relation)
          super
          q = self
          m = relation.model.arel_table

          mod = Module.new do
            define_method :not do |opts, *rest|
              if i18n_keys = q.extract_attributes(opts)
                opts = opts.with_indifferent_access
                query = q.create_not_query!(opts, i18n_keys, m)

                super(opts, *rest).where(query)
              else
                super(opts, *rest)
              end
            end
          end
          relation.mobility_where_chain.include(mod)
        end

        # Create +where+ query for options hash, translated keys and arel_table
        # @note This is a destructive operation, it will modify +opts+.
        #
        # @param [Hash] opts Hash of attribute/value pairs
        # @param [Array] keys Translated attribute names
        # @param [Arel::Table] arel_table Model or relation's arel table
        # @return [Arel::Node] Arel node to pass to +where+
        def create_where_query!(opts, keys, arel_table)
          keys.map { |key|
            column = arel_table[key]
            values = opts.delete(key)

            next has_locale(column).not if values.nil?

            Array.wrap(values).map { |value|
              contains_value(column, value)
            }.inject(&:or)
          }.inject(&:and)
        end

        # Create +not+ query for options hash, translated keys and arel_table
        # @note This is a destructive operation, it will modify +opts+.
        #
        # @param [Hash] opts Hash of attribute/value pairs
        # @param [Array] keys Translated attribute names
        # @param [Arel::Table] arel_table Model or relation's arel table
        # @return [Arel::Node] Arel node to pass to +where+
        def create_not_query!(opts, keys, arel_table)
          keys.map { |key|
            column = arel_table[key.to_sym]
            values = opts.delete(key)

            next has_locale(column) if values.nil?

            Array.wrap(values).map { |value|
              has_locale(column).and(contains_value(column, value).not)
            }.inject(&:and)
          }.inject(&:and)
        end

        private

        def contains_value(_column, _value)
          raise NotImplementedError
        end

        def has_locale(column)
          build_infix(:'?', column, quoted_locale)
{'fraction_non_alphanumeric': 0.08439661798616449, 'fraction_numerical': 0.0030745580322828594, 'mean_word_length': 2.014828544949027, 'pattern_counts': {'":': 0, '<': 3, '<?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': '823704', 'n_tokens_mistral': 1849, 'n_tokens_neox': 1766, 'n_words': 529}