Bagaimana cara menangani nama indeks yang terlalu panjang di migrasi ActiveRecord Ruby on Rails?

391

Saya mencoba menambahkan indeks unik yang dibuat dari kunci asing dari empat tabel terkait:

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

Keterbatasan database untuk nama indeks menyebabkan migrasi gagal. Inilah pesan kesalahannya:

Nama indeks 'index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id' pada tabel 'studi' terlalu panjang; batasnya adalah 64 karakter

Bagaimana saya bisa menangani ini? Bisakah saya menentukan nama indeks yang berbeda?

JJD
sumber

Jawaban:

590

Berikan :nameopsi untuk add_index, misalnya:

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

Jika menggunakan :indexopsi referencesdalam sebuah create_tableblok, dibutuhkan hash opsi yang sama add_indexseperti nilainya:

t.references :long_name, index: { name: :my_index }
fl00r
sumber
2
Menurut APIdock nama harus berupa string, bukan simbol
Jaco Pretorius
7
Sintaks turun untuk ini adalah remove_index :studies, :name => 'my_index'untuk siapa saja yang membutuhkannya
Kirk
On Rails 4.2.6bekerja dengan index: { name: :my_index }. Hanya namesaja tidak berhasil.
monteirobrena
174

Anda juga dapat mengubah nama indeks dalam definisi kolom dalam sebuah create_tableblok (seperti yang Anda dapatkan dari generator migrasi).

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end
Craig Walker
sumber
5
Perhatikan bahwa ini tidak membuat indeks multi-kolom dari pertanyaan awal; itu hanya menunjukkan bagaimana mempersingkat nama indeks yang panjang dari acreate_table
Craig Walker
6
Ini membantu saya ketika saya mencoba untuk membuat referensi polimorfik pada tabel nama spasi dengan indeks t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}dalam hal ini indeks sebenarnya multi-kolom (searchable_id dan searchable_type) dan penambahan namespace dalam nama yang dihasilkan menjadi sangat panjang .
mkrinblk
solusi yang bagus dan ringkas untuk saya. Terima kasih!
Sergio Belevskij
3
Dan seharusnya foreign_key: true, ini adalah solusi hebat karena ini adalah yang termudah untuk digunakan ketika Anda memiliki file migrasi yang dibuat dengan model:referencesformat generator rails
Abram
39

Dalam PostgreSQL, yang standar batas adalah 63 karakter . Karena nama indeks harus unik, senang memiliki sedikit konvensi. Saya menggunakan (saya mengubah contoh untuk menjelaskan konstruksi yang lebih kompleks):

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

Indeks normal adalah:

:index_studies_on_professor_id_and_user_id

Logikanya adalah:

  • index menjadi idx
  • Nama tabel tunggal
  • Tidak ada kata bergabung
  • Tidak _id
  • Sesuai abjad

Yang biasanya melakukan pekerjaan.

ekoologi
sumber
1
Terima kasih telah berbagi. Akan lebih baik jika Anda dapat menautkan dokumentasi Postgres untuk fakta keterbatasan.
JJD
Apakah kita memerlukan nama tabel dalam nama indeks karena indeks tetap milik tabel itu? Hanya ingin tahu apakah itu bermanfaat di tempat yang belum pernah saya lihat.
Joshua Pinter
Anda dapat memberi nama indeks apa yang Anda inginkan, tetapi saya pikir nama tabel dalam nama indeks membantu menjaga nama indeks unik (yang wajib), baik cakupannya dan meningkatkan beberapa keterbacaan pesan kesalahan.
ecoologic
22

Anda juga bisa melakukannya

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

seperti pada Ruby on Rails API .

tomascharad
sumber
6

Mirip dengan jawaban sebelumnya: Cukup gunakan kunci 'nama' dengan baris add_index reguler Anda:

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

Saya khawatir tidak ada solusi yang bekerja untuk saya. Mungkin karena saya menggunakan belongs_tomigrasi create_table untuk asosiasi polimorfik.

Saya akan menambahkan kode saya di bawah ini dan tautan ke solusi yang membantu saya kalau-kalau ada orang lain ketika mencari 'Nama indeks terlalu panjang' sehubungan dengan asosiasi polimorfik.

Kode berikut ini TIDAK bekerja untuk saya:

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

Kode ini tidak berfungsi untuk saya:

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

Ini adalah T&J SO yang membantu saya: https://stackoverflow.com/a/30366460/3258059

apa pun
sumber
1

Saya punya masalah ini, tetapi dengan timestampsfungsi. Itu membuat indeks secara otomatis pada updated_at yang melebihi batas 63 karakter:

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

Nama indeks 'index_toooooooooo_loooooooooooooooooooooooooooooong_on_updated_at' di atas meja 'toooooooooo_loooooooooooooooooooooooooooooooong' terlalu panjang; batasnya adalah 63 karakter

Saya mencoba menggunakan timestampsuntuk menentukan nama indeks:

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

Namun, ini mencoba menerapkan nama indeks untuk bidang updated_atdan created_at:

Nama indeks 'too_long_updated_at' on table 'tooooooooooo_loooooooooooooooooooooooooooooong' sudah ada

Akhirnya saya menyerah timestampsdan baru saja membuat cap waktu:

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

Ini bekerja tetapi saya ingin mendengar jika itu mungkin dengan timestampsmetode ini!

Fred Willmore
sumber
0

create_table: you_table_name do | t | t.referensi: pelajar, indeks: {name: 'name_for_studant_index'} t.referensi: guru, indeks: {name: 'name_for_teacher_index'} akhir

Adário Muatelembe
sumber
0

Saya punya proyek yang banyak menggunakan generator dan memerlukan ini otomatis, jadi saya menyalin index_namefungsi dari sumber rel untuk menimpanya. Saya menambahkan ini di 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

Itu menciptakan indeks seperti ix_assignments_on_case_id__project_id dan hanya memotongnya menjadi 63 karakter jika masih terlalu panjang. Itu masih akan menjadi non-unik jika nama tabel sangat panjang, tetapi Anda dapat menambahkan komplikasi seperti memperpendek nama tabel secara terpisah dari nama kolom atau benar-benar memeriksa keunikannya.

Catatan, ini dari proyek Rails 5.2; jika Anda memutuskan untuk melakukan ini, salin sumber dari versi Anda.

Jerph
sumber