Как мне обрабатывать слишком длинные имена индексов при переносе Ruby on Rails ActiveRecord?

391

Я пытаюсь добавить уникальный индекс, который создается из внешних ключей четырех связанных таблиц:

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

Ограничение базы данных на имя индекса приводит к сбою миграции. Вот сообщение об ошибке:

Имя индекса 'index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id' в таблице 'Studies' слишком длинное; ограничение составляет 64 символа

Как я могу справиться с этим? Могу ли я указать другое имя индекса?

JJD
источник

Ответы:

590

Предоставьте :nameвозможность add_index, например:

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

Если используется :indexопция on referencesв create_tableблоке, он принимает тот же хеш опций, что add_indexи его значение:

t.references :long_name, index: { name: :my_index }
fl00r
источник
2
Согласно APIdock, имя должно быть строкой, а не символом
Jaco Pretorius
7
Синтаксис down для этого предназначен remove_index :studies, :name => 'my_index'для всех, кому это нужно
Кирк
На Rails 4.2.6работает с index: { name: :my_index }. Только nameне сработало.
monteirobrena
174

Вы также можете изменить имя индекса в определениях столбцов в create_tableблоке (например, из генератора миграции).

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end
Крейг Уокер
источник
5
Обратите внимание, что это не создает многостолбцовый индекс из исходного вопроса; это просто демонстрация того, как сократить длинное индексное имя отcreate_table
Крейг Уолкер
6
Это помогло мне, когда я пытался создать полиморфную ссылку на таблицу с пространством имен с индексом, t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}в этом случае индекс был на самом деле многостолбцовым (searchable_id и searchable_type), и добавление пространства имен в сгенерированное имя стало очень длинным ,
mkrinblk
хорошее, компактное решение для меня. Спасибо!
Серхио Белевский
3
Также должно быть foreign_key: trueи, кстати, это отличное решение, поскольку его проще всего использовать, когда у вас есть файл миграции, созданный в model:referencesформате генератора рельсов
Abram
39

В PostgreSQL ограничение по умолчанию составляет 63 символа . Поскольку имена индексов должны быть уникальными, хорошо иметь небольшое соглашение. Я использую (я подправил пример, чтобы объяснить более сложные конструкции):

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

Нормальный индекс был бы:

:index_studies_on_professor_id_and_user_id

Логика будет такой:

  • index становится idx
  • Имя единственного стола
  • Нет присоединяющихся слов
  • нет _id
  • Алфавитный порядок

Который обычно делает работу.

ecoologic
источник
1
Спасибо, что поделился. Было бы неплохо, если бы вы могли связать документацию Postgres для факта ограничения.
JJD
Нужно ли нам имя таблицы в имени индекса, так как индекс все равно принадлежит этой таблице? Просто любопытно, если это полезно где-то я не видел.
Джошуа Пинтер
Вы можете присвоить индексу то, что хотите, но я думаю, что имя таблицы в имени индекса помогает сохранить уникальность имени индекса (что является обязательным), хорошую область видимости и улучшает читабельность некоторых сообщений об ошибках.
Экологическое
22

Вы также можете сделать

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

как в Ruby on Rails API .

tomascharad
источник
6

Аналогично предыдущему ответу: просто используйте клавишу «name» с вашей обычной строкой add_index:

def change
  add_index :studies, :user_id, name: 'my_index'
end
Надим Ясин
источник
2

Боюсь, ни одно из этих решений не сработало для меня. Возможно, потому что я использовал belongs_toв своей миграции create_table полиморфную ассоциацию.

Я добавлю свой код ниже и ссылку на решение, которое помогло мне в случае, если кто-то еще наткнется при поиске «Имя индекса слишком длинное» в связи с полиморфными ассоциациями.

Следующий код не работает для меня:

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

Этот код работал для меня:

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

Это SO Q & A, которая помогла мне: https://stackoverflow.com/a/30366460/3258059

whatapalaver
источник
1

У меня была эта проблема, но с timestampsфункцией. Он автоматически генерировал индекс на updated_at, который превысил ограничение в 63 символа:

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

Имя индекса 'index_toooooooooo_loooooooooooooooooooooooooooooong_on_updated_at' в таблице 'toooooooooo_loooooooooooooooooooooooooooooooong' слишком длинное; ограничение составляет 63 символа

Я пытался использовать timestampsдля указания имени индекса:

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

Тем не менее, это пытается применить имя индекса для обоих updated_atи created_atполей:

Имя индекса 'too_long_updated_at' в таблице 'toooooooooo_loooooooooooooooooooooooooooooong' уже существует

Наконец, я сдался timestampsи просто создал метки времени для долгого пути:

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

Это работает, но я хотел бы услышать, если это возможно с timestampsметодом!

Фред Уиллмор
источник
0

create_table: you_table_name do | t | t.references: ученик, индекс: {имя: 'name_for_studant_index'} t.references: учитель, индекс: {name: 'name_for_teacher_index'} конец

Адарио Муателембе
источник
0

У меня есть проект, который часто использует генераторы, и мне нужно, чтобы это было автоматически, поэтому я скопировал index_nameфункцию из источника rails, чтобы переопределить ее. Я добавил это в 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

Это создает индексы как ix_assignments_on_case_id__project_id и просто усекает его до 63 символов, если он все еще слишком длинный. Это по-прежнему будет неуникальным, если имя таблицы очень длинное, но вы можете добавить такие сложности, как сокращение имени таблицы отдельно от имен столбцов или фактическая проверка на уникальность.

Обратите внимание, это из проекта Rails 5.2; если вы решите сделать это, скопируйте исходный код из вашей версии.

Jerph
источник