In our previous article (linked below), we explored how to implement single-select enums in a Ruby on Rails application. In this post, we will extend that discussion to include the implementation of multi-select enums using arrays in Ruby on Rails with PostgreSQL.
Leveraging PostgreSQL 16 Enum Types in Rails 7+: A Step-by-Step Guide
Sulman Baig ・ Nov 25
Introduction to Multi-Select Enums
Enums are a convenient way to handle a set of predefined values in your application. Sometimes, however, you might need to associate multiple values with a single record. For example, an article might belong to several categories such as "blog," "snippet," or "vlog." In this case, using an array of enums is a suitable solution that provides the flexibility to assign multiple categories.
Why Use Enum Arrays?
PostgreSQL enum arrays offer several advantages:
Better performance compared to string arrays due to internal optimizations.
Type safety at the database level, reducing the chance of invalid data being saved.
Reduced storage space compared to text arrays, making your database more efficient.
Improved query performance with GIN (Generalized Inverted Index) indexing, which is particularly useful for fast lookups on array columns.
Setting Up the Migration
Let's implement a multi-category system for an Article
model. First, generate the migration:
rails g migration AddCategoriesToArticles categories:enum
Modify the generated migration to define an enum type and add the new column:
class AddCategoriesToArticles < ActiveRecord::Migration[7.2]
def change
create_enum :categories_enum, %w[
blog
snippet
vlog
]
add_column :articles, :categories, :enum, enum_type: :categories_enum, array: true, default: []
add_index :articles, :categories, using: 'gin'
end
end
In the migration above, the array: true
option enables multiple values to be stored in the categories
column. For optimal performance with array columns, PostgreSQL recommends using a GIN index to speed up querying.
Apply the migration:
rails db:migrate
Model Configuration
Next, define the enum in your Article
model with proper validations and methods:
class Article < ApplicationRecord
validates :categories, array_inclusion: { in: %w[blog snippet vlog] }
# Remove duplicates before saving
def categories=(types)
super(types.uniq)
end
# Convenience method for checking category presence
def has_category?(category)
categories.include?(category)
end
end
In this model, the array_inclusion
validation ensures that only the specified strings are included in the categories
array. The custom setter method categories=
removes duplicate values before saving them to the database, ensuring data integrity.
Usage Examples
Here are some examples of how you can work with multi-select enums in Rails:
# Create an article with multiple categories
article = Article.create(categories: ['blog', 'vlog'])
# Add a category
article.categories << 'snippet'
article.save
# Query articles with a specific category
articles_with_blog = Article.where("'blog' = ANY(categories)")
The array_inclusion
validation ensures that only permitted values are stored, while the custom setter prevents duplicate categories. For optimal query performance when filtering by categories, PostgreSQL will utilize the GIN index automatically.
Conclusion
PostgreSQL enum arrays in Rails provide a robust solution for handling multi-select fields with better performance and data integrity compared to string arrays. This pattern is particularly valuable for content management systems, tagging systems, or any scenario that requires multiple categorizations. By combining the use of enum arrays and proper indexing, you can ensure that your application remains performant as the data grows.
Happy Coding!
Top comments (0)