class AttrJson::Record::QueryBuilder

you don’t need to use it yourself, but you can.
Implementation class called by #jsonb_contains scope method. Ordinarily

def add_to_param_hash!(param_hash, key_path_str, value)

def add_to_param_hash!(param_hash, key_path_str, value)
  key_path = key_path_str.to_s.split(".")
  first_key, rest_keys = key_path.first, key_path[1..-1]
  attr_def = relation.attr_json_registry.fetch(first_key)
  value = if rest_keys.present?
    attr_def.type.value_for_contains_query(rest_keys, value)
  else
    attr_def.serialize(attr_def.cast value)
  end
  if value.kind_of?(Hash)
    param_hash[attr_def.store_key] ||= {}
    merge_param_hash!(param_hash[attr_def.store_key], value)
  else
    param_hash[attr_def.store_key] = value
  end
  # it's a mutator not functional don't you forget it.
  return nil
end

def contains_not_relation

def contains_not_relation
  contains_relation_impl do |relation, query, params|
    relation.where.not(query, params)
  end
end

def contains_relation

def contains_relation
  contains_relation_impl do |relation, query, params|
    relation.where(query, params)
  end
end

def contains_relation_impl

def contains_relation_impl
  result_relation = relation
  group_attributes_by_container.each do |container_attribute, attributes|
    param_hash = {}
    attributes.each do |key, value|
      add_to_param_hash!(param_hash, key, value)
    end
    result_relation = yield(result_relation, "#{relation.table_name}.#{container_attribute} @> (?)::jsonb", param_hash.to_json)
  end
  result_relation
end

def group_attributes_by_container

belonging to that container attribute.
returns a hash with keys container attributes, values hashes of attributes
def group_attributes_by_container
  @group_attributes_by_container ||= begin
    hash_by_container_attribute = {}
    input_attributes.each do |key_path, value|
      key = key_path.to_s.split(".").first
      attr_def = relation.attr_json_registry.fetch(key)
      container_attribute = attr_def.container_attribute
      hash_by_container_attribute[container_attribute] ||= {}
      hash_by_container_attribute[container_attribute][key_path] = value
    end
    hash_by_container_attribute
  end
end

def initialize(relation, input_attributes)

def initialize(relation, input_attributes)
  @relation = relation
  @input_attributes = input_attributes
end

def merge_param_hash!(original, new)

def merge_param_hash!(original, new)
  original.deep_merge!(new) do |key, old_val, new_val|
    if old_val.is_a?(Array) && old_val.first.is_a?(Hash) && new_val.is_a?(Array) && new_val.first.is_a?(Hash)
      [merge_param_hash!(old_val.first, new_val.first)]
    elsif old_val.is_a?(Hash) && new_val.is_a?(Hash)
      merge_param_hash!(old_val, new_val)
    else
      new_val
    end
  end
end