Comment gérer des noms d'index trop longs dans une migration Ruby on Rails ActiveRecord?

391

J'essaie d'ajouter un index unique qui est créé à partir des clés étrangères de quatre tables associées:

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

La limitation de la base de données pour le nom d'index entraîne l'échec de la migration. Voici le message d'erreur:

Le nom d'index 'index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id' sur la table 'studies' est trop long; la limite est de 64 caractères

Comment puis-je gérer cela? Puis-je spécifier un nom d'index différent?

JJD
la source

Réponses:

590

Offrez la :namepossibilité add_index, par exemple:

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

Si vous utilisez l' :indexoption on referencesdans un create_tablebloc, il prend le même hachage d'options add_indexque sa valeur:

t.references :long_name, index: { name: :my_index }
fl00r
la source
2
Selon APIdock, le nom doit être une chaîne, pas un symbole
Jaco Pretorius
7
La syntaxe descendante pour cela est remove_index :studies, :name => 'my_index'pour tous ceux qui en ont besoin
Kirk
On Rails 4.2.6fonctionne avec index: { name: :my_index }. Seulement namen'a pas fonctionné.
monteirobrena
174

Vous pouvez également modifier le nom de l'index dans les définitions de colonne d'un create_tablebloc (tel que celui obtenu à partir du générateur de migration).

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end
Craig Walker
la source
5
Notez que cela ne crée pas l'index multi-colonnes à partir de la question d'origine; il montre simplement comment raccourcir un nom d'index long à partir d'uncreate_table
Craig Walker
6
Cela m'a aidé lorsque j'essayais de créer une référence polymorphe sur une table à espacement de noms avec un index t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}dans ce cas, l'index était en fait une multi-colonne (searchable_id et searchable_type) et l'ajout de l'espace de noms dans le nom généré est devenu très long .
mkrinblk
belle solution compacte pour moi. Je vous remercie!
Sergio Belevskij
3
Devrait également avoir foreign_key: trueet d'ailleurs, c'est une excellente solution car il est le plus facile à utiliser lorsque vous avez un fichier de migration créé avec le model:referencesformat de générateur de rails
Abram
39

Dans PostgreSQL, la limite par défaut est de 63 caractères . Parce que les noms d'index doivent être uniques, c'est bien d'avoir une petite convention. J'utilise (j'ai peaufiné l'exemple pour expliquer des constructions plus complexes):

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

L'indice normal aurait été:

:index_studies_on_professor_id_and_user_id

La logique serait:

  • index devient idx
  • Nom de table singulier
  • Pas de mots joints
  • Non _id
  • Ordre alphabétique

Qui fait généralement le travail.

écolo
la source
1
Merci d'avoir partagé. Ce serait bien si vous pouviez lier la documentation Postgres pour le fait de la limitation.
JJD
Avons-nous besoin du nom de la table dans le nom de l'index puisque l'index appartient à cette table de toute façon? Juste curieux de savoir si c'est bénéfique quelque part que je n'ai pas vu.
Joshua Pinter
Vous pouvez nommer l'index comme vous le souhaitez, mais je pense que le nom de la table dans le nom d'index aide à garder le nom d'index unique (qui est obligatoire), bien délimité et améliore la lisibilité des messages d'erreur.
ecoologic
22

Vous pouvez aussi faire

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

comme dans l' API Ruby on Rails .

tomascharad
la source
6

Semblable à la réponse précédente: Utilisez simplement la touche 'nom' avec votre ligne add_index régulière:

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

Je crains qu'aucune de ces solutions ne fonctionne pour moi. Peut-être parce que j'utilisais belongs_todans ma migration create_table pour une association polymorphe.

J'ajouterai mon code ci-dessous et un lien vers la solution qui m'a aidé au cas où quelqu'un d'autre tomberait dessus lors de la recherche de 'Le nom d'index est trop long' en rapport avec les associations polymorphes.

Le code suivant n'a PAS fonctionné pour moi:

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

Ce code a fonctionné pour moi:

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

C'est le Q&A SO qui m'a aidé: https://stackoverflow.com/a/30366460/3258059

whatapalaver
la source
1

J'ai eu ce problème, mais avec la timestampsfonction. Il générait automatiquement un index sur updated_at qui dépassait la limite de 63 caractères:

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

Le nom d'index 'index_toooooooooo_loooooooooooooooooooooooooooooong_on_updated_at' sur la table 'toooooooooo_loooooooooooooooooooooooooooooong' est trop long; la limite est de 63 caractères

J'ai essayé d'utiliser timestampspour spécifier le nom d'index:

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

Cependant, cela essaie d'appliquer le nom d'index aux champs updated_atet created_at:

Le nom d'index 'too_long_updated_at' sur la table 'toooooooooo_loooooooooooooooooooooooooooooong' existe déjà

Enfin, j'ai abandonné et j'ai timestampsjuste créé les horodatages à long terme:

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

Cela fonctionne mais j'aimerais savoir si c'est possible avec la timestampsméthode!

Fred Willmore
la source
0

create_table: you_table_name do | t | t.references: studant, index: {name: 'name_for_studant_index'} t.references: teacher, index: {name: 'name_for_teacher_index'} end

Adário Muatelembe
la source
0

J'ai un projet qui utilise beaucoup de générateurs et dont j'avais besoin pour être automatique, j'ai donc copié la index_namefonction depuis la source des rails pour la remplacer. J'ai ajouté ceci dans 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

Il crée des index comme ix_assignments_on_case_id__project_id et le tronque simplement à 63 caractères s'il est encore trop long. Cela ne sera toujours pas unique si le nom de la table est très long, mais vous pouvez ajouter des complications telles que le raccourcissement du nom de la table séparément des noms de colonne ou la vérification de l'unicité.

Remarque, cela provient d'un projet Rails 5.2; si vous décidez de le faire, copiez la source depuis votre version.

Jerph
la source