Como lidar com nomes de índice muito longos em uma migração ActiveRecord do Ruby on Rails?

391

Estou tentando adicionar um índice exclusivo que é criado a partir das chaves estrangeiras de quatro tabelas associadas:

add_index :studies,
  ["user_id", "university_id", "subject_name_id", "subject_type_id"],
  :unique => true

A limitação do banco de dados para o nome do índice causa falha na migração. Aqui está a mensagem de erro:

O nome do índice 'index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id' na tabela 'estudos' é muito longo; o limite é de 64 caracteres

Como posso lidar com isso? Posso especificar um nome de índice diferente?

JJD
fonte

Respostas:

589

Forneça a :nameopção para add_index, por exemplo:

add_index :studies,
  ["user_id", "university_id", "subject_name_id", "subject_type_id"], 
  :unique => true,
  :name => 'my_index'

Se você estiver usando a :indexopção referencesem um create_tablebloco, terá o mesmo hash de opções add_indexque seu valor:

t.references :long_name, index: { name: :my_index }
fl00r
fonte
2
De acordo com o APIdock, o nome deve ser uma cadeia de caracteres, não um símbolo
Jaco Pretorius
7
A sintaxe para baixo para isto é remove_index :studies, :name => 'my_index'para quem precisa dele
Kirk
O On Rails 4.2.6trabalha com index: { name: :my_index }. Só namenão funcionou.
monteirobrena
174

Você também pode alterar o nome do índice nas definições de coluna em um create_tablebloco (como o obtido no gerador de migração).

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end
Craig Walker
fonte
5
Observe que isso não cria o índice de várias colunas da pergunta original; está apenas demonstrando como encurtar um nome de índice longo de umcreate_table
Craig Walker
6
Isso me ajudou quando eu estava tentando criar uma referência polimórfica em uma tabela espaçada de nomes com um índice. t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}Nesse caso, o índice era de fato uma coluna múltipla (searchable_id e searchable_type) e a adição do espaço para nome no nome gerado se tornou muito longa .
precisa saber é o seguinte
solução agradável e compacta para mim. Obrigado!
Sergio Belevskij
3
Também deve ter foreign_key: truee pela maneira, esta é uma grande solução, já que é o mais fácil de usar quando você tem um arquivo de migração criado com os trilhos gerador de model:referencesformato
Abram
39

No PostgreSQL, o limite padrão é de 63 caracteres . Como os nomes dos índices devem ser exclusivos, é bom ter uma pequena convenção. Eu uso (ajustei o exemplo para explicar construções mais complexas):

def change
  add_index :studies, [:professor_id, :user_id], name: :idx_study_professor_user
end

O índice normal teria sido:

:index_studies_on_professor_id_and_user_id

A lógica seria:

  • index torna-se idx
  • Nome da tabela singular
  • Sem palavras de união
  • Não _id
  • Ordem alfabética

O que geralmente faz o trabalho.

ecológico
fonte
11
Obrigado por compartilhar. Seria bom se você pudesse vincular a documentação do Postgres pelo fato da limitação.
JJD
Precisamos do nome da tabela no nome do índice, já que o índice pertence a essa tabela? Apenas curioso se isso é benéfico em algum lugar que eu não vi.
Joshua Pinter
Você pode nomear o índice como quiser, mas acho que o nome da tabela no nome do índice ajuda a manter o nome do índice exclusivo (que é obrigatório), com escopo bem definido e melhora a legibilidade das mensagens de erro.
ecoologic
22

Você também pode fazer

t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')

como na API Ruby on Rails .

tomascharad
fonte
6

Semelhante à resposta anterior: basta usar a tecla 'name' com sua linha add_index normal:

def change
  add_index :studies, :user_id, name: 'my_index'
end
Nadeem Yasin
fonte
2

Receio que nenhuma dessas soluções funcionou para mim. Talvez porque eu estivesse usando belongs_tona minha migração create_table para uma associação polimórfica.

Vou adicionar meu código abaixo e um link para a solução que me ajudou, caso mais alguém se depara ao pesquisar por 'O nome do índice é muito longo' em conexão com associações polimórficas.

O código a seguir NÃO funcionou para mim:

def change
  create_table :item_references do |t|
    t.text :item_unique_id
    t.belongs_to :referenceable, polymorphic: true
    t.timestamps
  end
  add_index :item_references, [:referenceable_id, :referenceable_type], name: 'idx_item_refs'
end

Este código funcionou para mim:

def change
  create_table :item_references do |t|
    t.text :item_unique_id
    t.belongs_to :referenceable, polymorphic: true, index: { name: 'idx_item_refs' }

    t.timestamps
  end
end

Este é o SO Q&A que me ajudou: https://stackoverflow.com/a/30366460/3258059

whatapalaver
fonte
1

Eu tive esse problema, mas com a timestampsfunção. Gerava automaticamente um índice em updated_at que excedia o limite de 63 caracteres:

def change
  create_table :toooooooooo_loooooooooooooooooooooooooooooong do |t|
    t.timestamps
  end
end

Nome do índice 'index_tooooooooo_looooooooooooooooooooooooooooong_on_updated_at' na tabela 'toooooooooo_loooooooooooooooooooooooooooooong' é muito longo; o limite é de 63 caracteres

Eu tentei usar timestampspara especificar o nome do índice:

def change
  create_table :toooooooooo_loooooooooooooooooooooooooooooong do |t|
    t.timestamps index: { name: 'too_loooooooooooooooooooooooooooooong_updated_at' }
  end
end

No entanto, isso tenta aplicar o nome do índice aos campos updated_ate created_at:

O nome do índice 'too_long_updated_at' na tabela 'toooooooooo_loooooooooooooooooooooooooooooong' já existe

Por fim, desisti timestampse apenas criei os carimbos de data e hora:

def change
  create_table :toooooooooo_loooooooooooooooooooooooooooooong do |t|
    t.datetime :updated_at, index: { name: 'too_long_on_updated_at' }
    t.datetime :created_at, index: { name: 'too_long_on_created_at' }
  end
end

Isso funciona, mas eu adoraria saber se é possível com o timestampsmétodo!

Fred Willmore
fonte
0

create_table: you_table_name do | t | t.references: aluno, índice: {name: 'name_for_studant_index'} t.references: professor, índice: {name: 'name_for_teacher_index'} end

Adário Muatelembe
fonte
0

Eu tenho um projeto que usa muito geradores e precisava que isso fosse automático, então copiei o index_name função da fonte de trilhos para substituí-la. Eu adicionei isso em config/initializers/generated_index_name.rb:

# make indexes shorter for postgres
require "active_record/connection_adapters/abstract/schema_statements"
module ActiveRecord
  module ConnectionAdapters # :nodoc:
    module SchemaStatements
      def index_name(table_name, options) #:nodoc:
        if Hash === options
          if options[:column]
            "ix_#{table_name}_on_#{Array(options[:column]) * '__'}".slice(0,63)
          elsif options[:name]
            options[:name]
          else
            raise ArgumentError, "You must specify the index name"
          end
        else
          index_name(table_name, index_name_options(options))
        end
      end
    end
  end
end

Cria índices como ix_assignments_on_case_id__project_id e apenas o trunca para 63 caracteres, se ainda for muito longo. Isso ainda não será exclusivo se o nome da tabela for muito longo, mas você pode adicionar complicações como encurtar o nome da tabela separadamente dos nomes das colunas ou verificar a exclusividade.

Observe que isso é de um projeto do Rails 5.2; se você decidir fazer isso, copie a fonte da sua versão.

Jerph
fonte