# How to Search Polymorphic Associations Using Ransack

> Learn how to search polymorphic associations with Ransack using the attribute naming pattern `association_of_Model_type_column_predicate`. Master complex queries efficiently.

- Repository: [ActiveRecord Hackery/ransack](https://github.com/activerecord-hackery/ransack)
- Tags: how-to-guide
- Published: 2026-02-23

---

**To search polymorphic associations using Ransack, use the attribute naming pattern `association_of_Model_type_column_predicate` (e.g., `notable_of_Person_type_name_eq`), which automatically generates SQL conditions for both the foreign-key and type columns.**

Searching across polymorphic `belongs_to` relationships in Ruby on Rails requires special handling because the database stores the association across two columns. The Ransack gem (activerecord-hackery/ransack) provides a specific naming convention that allows you to search polymorphic associations using standard search predicates while automatically managing the required type constraints.

## Understanding the Polymorphic Search Syntax

Ransack treats polymorphic associations similarly to standard associations, but requires a specific naming pattern to identify the concrete class you want to target. The pattern follows this structure:

```

<polymorphic_association>_of_<TargetModel>_type_<column>_<predicate>

```

For example, if you have a `Note` model with `belongs_to :notable, polymorphic: true`, and you want to search for notes where the notable Person's name equals "Ernie", you would use:

```

notable_of_Person_type_name_eq

```

This pattern breaks down into:

- **`<polymorphic_association>`** — the name of the `belongs_to` association (e.g., `notable`)
- **`of_<TargetModel>_type`** — specifies the concrete model class to query (e.g., `of_Person_type`)
- **`<column>`** — the attribute on the target model (e.g., `name`)
- **`<predicate>`** — any standard Ransack predicate such as `eq`, `cont`, or `lt`

## How Ransack Handles Polymorphic Joins

When you use this naming convention, Ransack automatically generates a join that includes both necessary conditions: matching the foreign key (`notable_id`) and restricting the type column (`notable_type`) to the specified class name.

According to the source code in [`lib/ransack/adapters/active_record/context.rb`](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/adapters/active_record/context.rb), the method `build_correlated_subquery` (lines 76-90) detects polymorphic associations by identifying an array of correlated keys. When Ransack encounters this array, it forces the type key to equal the model's class name, ensuring the generated SQL includes the type constraint alongside the foreign-key match.

The resulting SQL structure includes both conditions:

```sql
WHERE notes.notable_id = people.id
  AND notes.notable_type = 'Person'

```

## Searching Across Polymorphic Associations: Code Examples

### Basic Search on a Single Concrete Type

To find all notes whose notable Person has a specific name:

```ruby
search = Note.ransack(notable_of_Person_type_name_eq: 'Ernie')
notes = search.result

```

This generates SQL similar to:

```sql
SELECT "notes".*
FROM "notes"
INNER JOIN "people" ON "people"."id" = "notes"."notable_id"
WHERE "notes"."notable_type" = 'Person'
  AND "people"."name" = 'Ernie'

```

### Searching Across Multiple Polymorphic Types

You can combine conditions across different concrete types using the `_or_` combinator. This example searches for notes where the notable is either a Person named "Ernie" or an Article titled "Ernie":

```ruby
search = Note.ransack(
  notable_of_Person_type_name_or_notable_of_Article_type_title_eq: 'Ernie'
)
notes = search.result

```

The generated SQL uses LEFT OUTER JOINs for both tables and applies the appropriate type constraints:

```sql
SELECT "notes".*
FROM "notes"
LEFT OUTER JOIN "people" ON "people"."id" = "notes"."notable_id"
LEFT OUTER JOIN "articles" ON "articles"."id" = "notes"."notable_id"
WHERE ("notes"."notable_type" = 'Person' AND "people"."name" = 'Ernie')
   OR ("notes"."notable_type" = 'Article' AND "articles"."title" = 'Ernie')

```

### Using Array Predicates

You can use predicates that accept arrays, such as `in`:

```ruby

# Find notes whose notable Person's name is either "Ernie" or "Bert"

search = Note.ransack(notable_of_Person_type_name_in: %w[Ernie Bert])
notes = search.result

```

### Custom Predicates with Polymorphic Associations

Custom predicates work seamlessly with polymorphic attributes. First define the predicate:

```ruby
Ransack.configure do |config|
  config.add_predicate 'starts_with', 
                      arel_predicate: 'matches', 
                      formatter: ->(v) { "#{v}%" }, 
                      validator: ->(v) { v.present? }
end

```

Then use it with the polymorphic pattern:

```ruby
search = Note.ransack(notable_of_Article_type_title_starts_with: 'Rails')
notes = search.result

```

## Key Implementation Files in Ransack

Understanding where this logic lives helps when debugging complex queries:

- **[`lib/ransack/adapters/active_record/context.rb`](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/adapters/active_record/context.rb)** — Contains the `build_correlated_subquery` method (lines 76-90) that handles the two-part correlated key logic for polymorphic joins, forcing the type condition when an array of keys is detected.
- **[`lib/ransack/search.rb`](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/search.rb)** — Orchestrates the parsing of attribute strings and builds the `Nodes::Condition` objects that represent polymorphic search criteria.
- **[`lib/ransack/nodes/condition.rb`](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/nodes/condition.rb)** — Represents the final condition objects that include the full polymorphic attribute name.
- **[`spec/ransack/search_spec.rb`](https://github.com/activerecord-hackery/ransack/blob/main/spec/ransack/search_spec.rb)** (lines 9-31) — Contains test cases demonstrating the attribute naming convention, including examples like `notable_of_Person_type_name_eq` and cross-type OR conditions.
- **[`spec/support/schema.rb`](https://github.com/activerecord-hackery/ransack/blob/main/spec/support/schema.rb)** (lines 57-68) — Defines the test schema with `Note` models using polymorphic `belongs_to :notable`.

## Summary

- Use the pattern `<association>_of_<Model>_type_<column>_<predicate>` to search polymorphic associations in Ransack.
- Ransack automatically generates both the foreign-key and type-column conditions required for correct polymorphic joins.
- The core logic resides in `Ransack::Adapters::ActiveRecord::Context#build_correlated_subquery`, which detects polymorphic associations through correlated key arrays.
- You can search across multiple concrete types in a single query using the `_or_` combinator in the attribute name.
- All standard Ransack predicates, including custom ones, work with the polymorphic naming convention.

## Frequently Asked Questions

### What is the exact syntax for searching polymorphic associations in Ransack?

The syntax follows the pattern `association_of_Model_type_column_predicate`. For example, if you have a `Comment` model with `belongs_to :commentable, polymorphic: true` and want to find comments on posts with a specific title, use `commentable_of_Post_type_title_eq`. Ransack parses this string to identify the association, target model, column, and predicate.

### How does Ransack handle the type column in polymorphic searches?

Ransack automatically injects the type condition when it detects a polymorphic attribute pattern. In [`lib/ransack/adapters/active_record/context.rb`](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/adapters/active_record/context.rb), the `build_correlated_subquery` method checks for an array of correlated keys (foreign key and type key). When found, it forces the type key to equal the target model's class name, generating SQL like `notable_type = 'Person'` alongside the join condition.

### Can I search across multiple polymorphic types in a single Ransack query?

Yes. Use the `_or_` combinator between different type specifications. For example: `notable_of_Person_type_name_or_notable_of_Company_type_name_eq`. This creates a LEFT OUTER JOIN for each table and applies the appropriate type constraints in the WHERE clause, allowing you to match records from different concrete types in one search.

### Do custom Ransack predicates work with polymorphic associations?

Yes. Any custom predicate defined via `Ransack.configure` works with polymorphic attributes using the standard naming pattern. Simply append your custom predicate to the end of the polymorphic attribute string, such as `notable_of_Article_type_title_starts_with` if you have defined a `starts_with` predicate.