# Implementing Multi-Select Enums in Ruby on Rails with PostgreSQL

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.

%[https://sulmanweb.com/postgresql-enum-types-rails-7-guide] 

## 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:

```bash
rails g migration AddCategoriesToArticles categories:enum
```

Modify the generated migration to define an enum type and add the new column:

```ruby
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:

```bash
rails db:migrate
```

## Model Configuration

Next, define the enum in your `Article` model with proper validations and methods:

```ruby
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:

```ruby
# 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!
