¿Cómo manejo nombres de índice demasiado largos en una migración de Ruby on Rails ActiveRecord?

391

Estoy tratando de agregar un índice único que se crea a partir de las claves externas de cuatro tablas asociadas:

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

La limitación de la base de datos para el nombre del índice hace que la migración falle. Aquí está el mensaje de error:

El nombre de índice 'index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id' en la tabla 'estudios' es demasiado largo; el límite es de 64 caracteres

¿Cómo puedo manejar esto? ¿Puedo especificar un nombre de índice diferente?

JJD
fuente

Respuestas:

590

Proporcione la :nameopción de add_index, por ejemplo:

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

Si usa la :indexopción referencesen un create_tablebloque, toma el mismo hash de opciones add_indexque su valor:

t.references :long_name, index: { name: :my_index }
fl00r
fuente
2
Según APIdock, el nombre debe ser una cadena, no un símbolo
Jaco Pretorius
77
La sintaxis inactiva para esto es remove_index :studies, :name => 'my_index'para cualquiera que la necesite
Kirk
On Rails 4.2.6trabaja con index: { name: :my_index }. Solo nameque no funcionó.
monteirobrena
174

También puede cambiar el nombre del índice en las definiciones de columna dentro de un create_tablebloque (como el que obtiene del generador de migración).

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end
Craig Walker
fuente
55
Tenga en cuenta que esto no crea el índice de varias columnas a partir de la pregunta original; solo está demostrando cómo acortar un nombre de índice largo de uncreate_table
Craig Walker
66
Esto me ayudó cuando estaba tratando de crear una referencia polimórfica en una tabla espaciada de nombres con un índice; t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}en este caso, el índice era de hecho una columna múltiple (searchable_id y searchable_type) y la adición del espacio de nombres en el nombre generado se hizo muy larga. .
mkrinblk
Una solución agradable y compacta para mí. ¡Gracias!
Sergio Belevskij
3
También debería haberlo foreign_key: truey, por cierto, esta es una gran solución, ya que es la más fácil de usar cuando tienes un archivo de migración creado con el model:referencesformato generador de rieles
Abram
39

En PostgreSQL, el límite predeterminado es de 63 caracteres . Debido a que los nombres de índice deben ser únicos, es bueno tener una pequeña convención. Yo uso (modifiqué el ejemplo para explicar construcciones más complejas):

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

El índice normal habría sido:

:index_studies_on_professor_id_and_user_id

La lógica sería:

  • index se convierte idx
  • Nombre de tabla singular
  • Sin palabras unidas
  • No _id
  • Orden alfabetico

Que generalmente hace el trabajo.

ecoologico
fuente
1
Gracias por compartir. Sería bueno si pudiera vincular la documentación de Postgres para el hecho de la limitación.
JJD
¿Necesitamos el nombre de la tabla en el nombre del índice ya que el índice pertenece a esa tabla de todos modos? Es curioso si eso es beneficioso en algún lugar que no haya visto.
Joshua Pinter
Puede nombrar el índice como desee, pero creo que el nombre de la tabla en el nombre del índice ayuda a mantener el nombre del índice único (que es obligatorio), bien definido y mejora la legibilidad de algunos mensajes de error.
ecoologic
22

También puedes hacer

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

como en la API de Ruby on Rails .

tomascharad
fuente
6

Similar a la respuesta anterior: solo use la tecla 'nombre' con su línea regular add_index:

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

Me temo que ninguna de estas soluciones funcionó para mí. Quizás porque estaba usando belongs_toen mi migración create_table para una asociación polimórfica.

Agregaré mi código a continuación y un enlace a la solución que me ayudó en caso de que alguien más se tope al buscar 'El nombre del índice es demasiado largo' en relación con las asociaciones polimórficas.

El siguiente código NO me funcionó:

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

Este código me funcionó:

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

Estas son las preguntas y respuestas de SO que me ayudaron: https://stackoverflow.com/a/30366460/3258059

whatapalaver
fuente
1

Tuve este problema, pero con la timestampsfunción. Generaba automáticamente un índice en updated_at que excedía el límite de 63 caracteres:

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

El nombre del índice 'index_toooooooooo_loooooooooooooooooooooooooooooong_on_updated_at' en la tabla 'toooooooooo_loooooooooooooooooooooooooooooong' es demasiado largo; el límite es de 63 caracteres

Traté de usar timestampspara especificar el nombre del índice:

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

Sin embargo, esto intenta aplicar el nombre de índice a los campos updated_aty created_at:

El nombre de índice 'too_long_updated_at' en la tabla 'toooooooooo_loooooooooooooooooooooooooooooong' ya existe

Finalmente me di por vencido timestampsy creé las marcas de tiempo en el largo camino:

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

¡Esto funciona pero me encantaría saber si es posible con el timestampsmétodo!

Fred Willmore
fuente
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
fuente
0

Tengo un proyecto que usa mucho los generadores y necesitaba que esto fuera automático, así que copié la index_namefunción de la fuente de rieles para anularla. Agregué esto en 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

Crea índices como ix_assignments_on_case_id__project_idy simplemente lo trunca a 63 caracteres si aún es demasiado largo. Eso seguirá siendo no único si el nombre de la tabla es muy largo, pero puede agregar complicaciones como acortar el nombre de la tabla por separado de los nombres de las columnas o verificar la unicidad.

Tenga en cuenta que esto es de un proyecto Rails 5.2; Si decide hacer esto, copie la fuente de su versión.

Jerph
fuente