Jak obsłużyć zbyt długie nazwy indeksów w migracji ActiveRecord Ruby on Rails?

391

Próbuję dodać unikalny indeks, który jest tworzony z kluczy obcych czterech powiązanych tabel:

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

Ograniczenie bazy danych dla nazwy indeksu powoduje niepowodzenie migracji. Oto komunikat o błędzie:

Nazwa indeksu „index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id” w tabeli „studies” jest za długa; limit wynosi 64 znaki

Jak sobie z tym poradzić? Czy mogę podać inną nazwę indeksu?

JJD
źródło

Odpowiedzi:

589

Zapewnij :nameopcję add_indexnp .:

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

Jeśli używasz :indexopcji włączonej referencesw create_tablebloku, pobiera ten sam skrót opcji, add_indexco jego wartość:

t.references :long_name, index: { name: :my_index }
fl00r
źródło
2
Według APIdock nazwa musi być łańcuchem, a nie symbolem
Jaco Pretorius,
7
Down składnia tego jest remove_index :studies, :name => 'my_index'dla każdego, kto jej potrzebuje
Kirk
On Rails 4.2.6współpracuje z index: { name: :my_index }. Tylko namenie działało.
monteirobrena
174

Możesz także zmienić nazwę indeksu w definicjach kolumn w ciągu create_table bloku (np. Z generatora migracji).

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end
Craig Walker
źródło
5
Zauważ, że to nie tworzy indeksu wielokolumnowego z pierwotnego pytania; pokazuje tylko, jak skrócić długą nazwę indeksu zcreate_table
Craig Walker
6
Pomogło mi to, gdy próbowałem utworzyć odwołanie polimorficzne w tabeli z odstępami między nazwami i indeksem. t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}W tym przypadku indeks był w rzeczywistości wielokolumnową (id_wyszukiwalny i typ_wyszukiwalny), a dodanie przestrzeni nazw do wygenerowanej nazwy stało się bardzo długie .
mkrinblk
fajne, kompaktowe rozwiązanie dla mnie. Dziękuję Ci!
Sergio Belevskij
3
Powinien też mieć, foreign_key: truea przy okazji, jest to świetne rozwiązanie, ponieważ jest najłatwiejsze w użyciu, gdy masz plik migracji utworzony w model:referencesformacie generatora szyn
Abram
39

W PostgreSQL domyślny limit wynosi 63 znaków . Ponieważ nazwy indeksów muszą być unikalne, miło jest mieć trochę konwencji. Używam (poprawiłem przykład, aby wyjaśnić bardziej złożone konstrukcje):

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

Normalny wskaźnik byłby:

:index_studies_on_professor_id_and_user_id

Logika wyglądałaby następująco:

  • index staje się idx
  • Nazwa pojedynczej tabeli
  • Brak łączących słów
  • Nie _id
  • Kolejność alfabetyczna

Co zwykle wykonuje pracę.

ekologiczny
źródło
1
Dzięki za udostępnienie. Byłoby miło, gdybyś mógł połączyć dokumentację Postgres z faktem dotyczącym ograniczenia.
JJD
Czy potrzebujemy nazwy tabeli w nazwie indeksu, ponieważ indeks i tak należy do tej tabeli? Ciekawe, czy to jest korzystne, gdzieś nie widziałem.
Joshua Pinter
Możesz nazwać indeks, jak chcesz, ale myślę, że nazwa tabeli w nazwie indeksu pomaga zachować unikatową nazwę indeksu (która jest obowiązkowa), dobrze zakresem i poprawia czytelność niektórych komunikatów o błędach.
ekologiczny
22

Możesz też zrobić

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

jak w interfejsie API Ruby on Rails .

tomascharad
źródło
6

Podobna do poprzedniej odpowiedzi: po prostu użyj klawisza „name” ze zwykłą linią add_index:

def change
  add_index :studies, :user_id, name: 'my_index'
end
Nadeem Yasin
źródło
2

Obawiam się, że żadne z tych rozwiązań nie działało dla mnie. Być może dlatego, że używałembelongs_to z mojej migracji create_table dla skojarzenia polimorficznego.

Dodam mój kod poniżej i link do rozwiązania, które pomogło mi na wypadek, gdyby ktoś natknął się na hasło „Nazwa indeksu jest za długa” w związku z powiązaniami polimorficznymi.

Poniższy kod NIE działał dla mnie:

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

Ten kod DID działał dla mnie:

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

Oto SO pytania i odpowiedzi, które pomogły mi: https://stackoverflow.com/a/30366460/3258059

whatapalaver
źródło
1

Miałem ten problem, ale z timestampsfunkcją. Automatycznie generował indeks w updated_at, który przekroczył limit 63 znaków:

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

Nazwa indeksu „index_toooooooooo_loooooooooooooooooooooooooooooong_on_updated_at” w tabeli „toooooooooo_loooooooooooooooooooooooooooooong” jest za długa; limit wynosi 63 znaki

Próbowałem użyć timestampsdo określenia nazwy indeksu:

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

Próbuje to jednak zastosować nazwę indeksu do pól updated_ati created_at:

Nazwa indeksu „too_long_updated_at” w tabeli „toooooooooo_loooooooooooooooooooooooooooooong już istnieje

W końcu zrezygnowałem timestampsi po prostu stworzyłem znaczniki czasu:

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

To działa, ale chciałbym usłyszeć, czy jest to możliwe dzięki tej timestampsmetodzie!

Fred Willmore
źródło
0

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

Adário Muatelembe
źródło
0

Mam projekt, który często korzysta z generatorów i potrzebuję go do automatycznego, więc skopiowałem index_namefunkcję ze źródła szyn, aby ją zastąpić. Dodałem to w 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

Tworzy indeksy takie jak ix_assignments_on_case_id__project_id i po prostu obcina go do 63 znaków, jeśli nadal jest zbyt długi. To nadal nie będzie unikalne, jeśli nazwa tabeli jest bardzo długa, ale możesz dodać komplikacje, takie jak skrócenie nazwy tabeli osobno od nazw kolumn lub faktycznie sprawdzenie wyjątkowości.

Uwaga: pochodzi z projektu Rails 5.2; jeśli zdecydujesz się to zrobić, skopiuj źródło ze swojej wersji.

Jerph
źródło