Wie gehe ich mit zu langen Indexnamen in einer Ruby on Rails ActiveRecord-Migration um?

391

Ich versuche, einen eindeutigen Index hinzuzufügen, der aus den Fremdschlüsseln von vier zugeordneten Tabellen erstellt wird:

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

Die Einschränkung der Datenbank für den Indexnamen führt dazu, dass die Migration fehlschlägt. Hier ist die Fehlermeldung:

Der Indexname 'index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id' in der Tabelle 'study' ist zu lang. Das Limit beträgt 64 Zeichen

Wie kann ich damit umgehen? Kann ich einen anderen Indexnamen angeben?

JJD
quelle

Antworten:

589

Stellen Sie die :nameOption bereit add_index, z.

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

Wenn Sie die :indexOption referencesin einem create_tableBlock aktivieren, wird derselbe Options-Hash als add_indexWert verwendet:

t.references :long_name, index: { name: :my_index }
fl00r
quelle
2
Laut APIdock muss der Name eine Zeichenfolge sein, kein Symbol
Jaco Pretorius
7
Die Down-Syntax dafür ist remove_index :studies, :name => 'my_index'für jeden, der sie braucht
Kirk
On Rails 4.2.6funktioniert mit index: { name: :my_index }. Nur namehat nicht funktioniert.
Monteirobrena
174

Sie können den Indexnamen auch in Spaltendefinitionen innerhalb eines create_tableBlocks ändern (z. B. vom Migrationsgenerator).

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end
Craig Walker
quelle
5
Beachten Sie, dass dadurch der mehrspaltige Index nicht aus der ursprünglichen Frage erstellt wird. Es zeigt nur, wie man einen langen Indexnamen von einemcreate_table
Craig Walker
6
Dies hat mir geholfen, als ich versucht habe, eine polymorphe Referenz für eine Tabelle mit Namensabstand und einem Index zu erstellen. t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}In diesem Fall war der Index tatsächlich mehrspaltig (searchable_id und searchable_type), und das Hinzufügen des Namespace zum generierten Namen wurde sehr lang .
mkrinblk
schöne, kompakte Lösung für mich. Vielen Dank!
Sergio Belevskij
3
Sollte auch haben foreign_key: trueund übrigens, dies ist eine großartige Lösung, da es am einfachsten zu verwenden ist, wenn Sie eine Migrationsdatei mit dem Rails-Generator- model:referencesFormat erstellt haben
Abram
39

In PostgreSQL beträgt das Standardlimit 63 Zeichen . Da Indexnamen eindeutig sein müssen, ist es schön, eine kleine Konvention zu haben. Ich benutze (ich habe das Beispiel optimiert, um komplexere Konstruktionen zu erklären):

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

Der normale Index wäre gewesen:

:index_studies_on_professor_id_and_user_id

Die Logik wäre:

  • index wird idx
  • Name der einzelnen Tabelle
  • Keine verbindenden Worte
  • Nein _id
  • Alphabetischer Reihenfolge

Welches macht normalerweise den Job.

ökoologisch
quelle
1
Danke für das Teilen. Wäre schön, wenn Sie die Postgres-Dokumentation für die Einschränkung Tatsache verknüpfen könnten.
JJD
Benötigen wir den Tabellennamen im Indexnamen, da der Index sowieso zu dieser Tabelle gehört? Nur neugierig, ob das irgendwo von Vorteil ist, was ich nicht gesehen habe.
Joshua Pinter
Sie können den Index so benennen, wie Sie möchten, aber ich denke, der Tabellenname im Indexnamen hilft dabei, den Indexnamen eindeutig zu halten (was obligatorisch ist), einen guten Umfang zu haben und die Lesbarkeit von Fehlermeldungen zu verbessern.
Ökoologie
22

Sie können auch tun

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

wie in der Ruby on Rails-API .

Tomascharad
quelle
6

Ähnlich wie bei der vorherigen Antwort: Verwenden Sie einfach den Schlüssel 'name' mit Ihrer regulären Zeile add_index:

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

Ich fürchte, keine dieser Lösungen hat bei mir funktioniert. Vielleicht, weil ich belongs_toin meiner create_table-Migration eine polymorphe Assoziation verwendet habe.

Ich füge unten meinen Code und einen Link zu der Lösung hinzu, die mir geholfen hat, falls jemand bei der Suche nach "Indexname ist zu lang" im Zusammenhang mit polymorphen Assoziationen stolpert.

Der folgende Code hat bei mir NICHT funktioniert:

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

Dieser Code hat bei mir funktioniert:

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

Dies ist die SO Q & A, die mir geholfen hat: https://stackoverflow.com/a/30366460/3258059

Whatapalaver
quelle
1

Ich hatte dieses Problem, aber mit der timestampsFunktion. Es wurde automatisch ein Index für update_at generiert, der die 63-Zeichen-Grenze überschritten hat:

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

Der Indexname 'index_toooooooooo_loooooooooooooooooooooooooooooong_on_updated_at' auf der Tabelle 'toooooooooo_loooooooooooooooooooooooooooong' ist zu lang; Das Limit beträgt 63 Zeichen

Ich habe versucht, den Indexnamen timestampsanzugeben:

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

Allerdings versucht dies die Indexnamen für beide die anzuwenden updated_atund created_atFelder:

Der Indexname 'too_long_updated_at' in der Tabelle 'toooooooooo_loooooooooooooooooooooooooooong' existiert bereits

Schließlich habe ich aufgegeben timestampsund nur die Zeitstempel auf dem langen Weg erstellt:

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

Das funktioniert, aber ich würde gerne hören, ob es mit der timestampsMethode möglich ist!

Fred Willmore
quelle
0

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

Adário Muatelembe
quelle
0

Ich habe ein Projekt, das häufig Generatoren verwendet, und dies musste automatisch erfolgen. Deshalb habe ich die index_nameFunktion aus der Rails-Quelle kopiert , um sie zu überschreiben. Ich habe dies hinzugefügt in 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

Es erstellt Indizes wie ix_assignments_on_case_id__project_idund schneidet sie nur auf 63 Zeichen ab, wenn sie noch zu lang sind. Das ist immer noch nicht eindeutig, wenn der Tabellenname sehr lang ist. Sie können jedoch Komplikationen hinzufügen, z. B. den Tabellennamen getrennt von den Spaltennamen kürzen oder tatsächlich auf Eindeutigkeit prüfen.

Beachten Sie, dass dies aus einem Rails 5.2-Projekt stammt. Wenn Sie sich dazu entschließen, kopieren Sie die Quelle aus Ihrer Version.

Jerph
quelle