A migration que transformou as colunas bilíngues em JSONB

Prévia grátis 3 min de leitura

Todo título, descrição e dica bilíngue que os cursos anteriores citaram

Catalog::Course#title e #description, Catalog::Lesson#title e #content_markdown, Catalog::Challenge#instruction, #solution e #hints: todas essas colunas são jsonb. A maioria não começou assim. courses.title, lessons.title, lessons.content_markdown e challenges.instruction nasceram como colunas string e text comuns e foram convertidas um dia depois; hints e solution vieram mais tarde e já foram criadas como jsonb. db/migrate/20260227224007_convert_course_fields_to_jsonb.rb é a migration em que courses.title deixou de ser uma string comum:

# db/migrate/20260227224007_convert_course_fields_to_jsonb.rb
def up
  add_column :courses, :title_jsonb, :jsonb, default: {}
  add_column :courses, :description_jsonb, :jsonb, default: {}

  execute <<-SQL
    UPDATE courses
    SET title_jsonb = jsonb_build_object('en', title, 'pt-BR', title),
        description_jsonb = jsonb_build_object('en', description, 'pt-BR', description)
  SQL

  change_column_null :courses, :title_jsonb, false

  remove_column :courses, :title
  remove_column :courses, :description

  rename_column :courses, :title_jsonb, :title
  rename_column :courses, :description_jsonb, :description
end

Lendo a migration

Ela faz cinco movimentos, em ordem. Primeiro acrescenta uma coluna jsonb temporária ao lado de cada coluna antiga. Depois preenche todas as linhas com jsonb_build_object, uma função do Postgres que monta um objeto JSON no próprio SQL. Ela embrulha a única string existente sob duas chaves, 'en' e 'pt-BR', as duas iguais no começo porque ainda não havia tradução. Em terceiro lugar, torna a nova coluna de título NOT NULL, já que toda linha tem valor. Em quarto, apaga as colunas de string antigas e, por fim, renomeia as novas colunas para os nomes antigos.

A tabela termina com os mesmos nomes de coluna, title e description, guardando JSON em vez de texto puro.

jsonb_build_object, e por que a migration não faz só um cast

title::jsonb sozinho falharia, porque um texto como Some Title não é JSON válido. Mesmo uma string com aspas viraria uma string JSON, e não o objeto {"en": ..., "pt-BR": ...} que o código do app espera. jsonb_build_object('en', title, 'pt-BR', title) monta o objeto chave por chave dentro do Postgres, num único UPDATE sobre todas as linhas, sem loop em Ruby pelos cursos.

A migration sabe se desfazer

# mesmo arquivo
def down
  add_column :courses, :title_string, :string
  add_column :courses, :description_text, :text

  execute <<-SQL
    UPDATE courses
    SET title_string = title->>'en',
        description_text = description->>'en'
  SQL

  change_column_null :courses, :title_string, false
  remove_column :courses, :title
  remove_column :courses, :description
  rename_column :courses, :title_string, :title
  rename_column :courses, :description_text, :description
end

Os mesmos cinco movimentos rodam ao contrário, com uma peça nova: title->>'en', o operador de seta do Postgres, que tira o valor de 'en' do objeto JSON como texto puro. A aula 4 trata desse operador. Por enquanto, repare que ele espelha o jsonb_build_object: um monta um objeto JSON a partir de valores simples, o outro extrai um valor simples de um objeto JSON.

O que este curso cobre

Sete aulas, todas sobre código deste app. A aula 2 trata do ActiveRecord::Store, a ferramenta do próprio Rails para esse tipo de coluna, numa tabela descartável, já que este app não a usa. A aula 3 mostra por que o app escreveu o próprio concern Translatable no lugar dela. A aula 4 consulta JSONB direto em SQL com o mesmo operador ->>, em queries de produção. A aula 5 trata de índices GIN: o que os índices do app aceleram e o que não aceleram. A aula 6 trata do principal motivo de existir do JSONB, dados de formato não fixo, numa parte do app que está rodando agora.

Aula 1 de 7 · grátis

Continue com a aula 2

store_accessor: a ferramenta do próprio Rails para esse tipo de coluna

O store_accessor do Rails numa coluna jsonb, testado numa tabela descartável, incluindo chaves symbol que retornam nil após recarregar.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...