Skip to content
Carlos Silva edited this page Aug 11, 2025 · 6 revisions

Additional Arel features. Other features of this gem do not require these to be available or configured in any way. They are indeed used internally, but without the external exposure. Feel free to change, configure, and use them as desired.

Arel Function Node builder

The Arel::Nodes::NamedFunction.new is not very developer-friendly, and there are a few things that are sometimes harder to do with it. Because of that, the gem has the Torque::PostgreSQL::Function helper to shortcut and facilitate such node building.

You can make this helper available anywhere by just setting the arel.expose_function_helper_on config. For example, setting to 'PG::Fn', will make the following features exposed in it:

Bind value

When building arel notes that do include raw values, it is more accurate to provide such values via binds. They work better with prepared statements and queries can have improved caching. However, it's not really easy to build them, so these methods facilitate that:

PG::Fn.bind(:title, 'Example', ActiveRecord::Type::String.new)    # It returns a bind param for your query
PG::Fn.bind_for(Video, :title, 'Example')                         # It pulls the type from the active model
PG::Fn.bind_with(Video.arel_table['title'], 'Example')            # It pulls the type from the type caster of the arel attribute
Bind type

The bind_type helper is mostly used for binding values that are not directly associated with columns. Most commonly used when given arguments to functions. It will use the type given as the second argument (Symbols will be translated into a proper ActiveModel::Type) or translate the Ruby class into the proper ActiveModel type.

PG::Fn.bind_type(123)                    # Creates a ActiveModel::Type::Integer bind param
PG::Fn.bind_type(123, :string)           # Creates a ActiveModel::Type::String bind param
PG::Fn.bind_type(123, cast: :numeric)    # Writes the query casting the bind param, as in `$1::numeric`

Infix operation

This is a shortcut for the Arel::Nodes::InfixOperation, which is widely used in the gem, so it needed to be easily accessible.

                                                                                  # The operator is not checked!
PG::Fn.infix('@!', Video.arel_table['title'], Video.arel_table['description'])    # Produces "videos"."title" @! "videos"."description"

Concat

A shortcut to build a proper chain of concatenation (||) nodes.

PG::Fn.concat(Video.arel_table['title'])                                                                   # Produces "videos"."title"
PG::Fn.concat(Video.arel_table['title'], Video.arel_table['subtitle'])                                     # Produces "videos"."title" || "videos"."subtitle"
PG::Fn.concat(Video.arel_table['title'], Video.arel_table['subtitle'], Video.arel_table['description'])    # Produces "videos"."title" || "videos"."subtitle" || "videos"."description"

Group By

A helper that allows calling .group on a query, giving an Arel node, which will cause calculation queries (like .count) to be performed correctly.

# SELECT COUNT(*) AS "count_all", "users"."deleted_at" IS NULL AS "deleted" FROM "users" GROUP BY "deleted"
User.group(PG::Fn.group_by(User.arel_table['deleted_at'].eq(nil), 'deleted')).count

Method missing

You can call any function on this helper that it will turn it into a Arel::Nodes::NamedFunction with the given arguments.

                                                                            # The existence of the function is not checked!
PG::Fn.lower(Video.arel_table['title'])                                     # Produces LOWER("videos"."title")
PG::Fn.coalesce(Video.arel_table['title'], Video.arel_table['subtitle'])    # Produces COALESCE("videos"."title", "videos"."subtitle")
PG::Fn.something_else(Video.arel_table['title'])                            # Produces SOMETHING_ELSE("videos"."title")

Inflix operators

PostgreSQL allows a bunch of custom operators while working with Arrays, Ranges, and Hashes.

Now with the arel.infix_operators config, you can set up all the ones that are used and take advantage of the method being available on any Arel::Nodes::Node and Arel::Attributes::Attribute. These are the ones configured by default (method => operator):

.contained_by        => <@
.has_key             => ?
.has_all_keys        => ?&
.has_any_keys        => ?|
.strictly_left       => <<
.strictly_right      => >>
.doesnt_right_extend => &<
.doesnt_left_extend  => &>
.adjacent_to         => -|-

Cast values

One of the things that allowed array associations and period operations is the ability to cast things from one type to another directly on Arel. This means that time columns can be casted into dates and have all the capabilities of a normal Arel node. Rails 8.0.2 introduces the same method with a small difference, so now there are two methods available: .cast and .pg_cast.

If you are used to Arel, to cast something, it's pretty simple:

Event.arel_table[:created_at].cast(:date)

which will produce

CAST("events"."created_at" AS date)   -- Rails >= 8.0.2
"events"."created_at"::date           -- Rails < 8.0.2 or using .pg_cast
Clone this wiki locally