class

Takarik::Data::QueryBuilder(T)

Inherits Reference < Object

Constants

DEFAULT_ORDER = :asc
ORDER_IGNORE_MESSAGE = "Scoped order is ignored, use :cursor with :order to configure custom order."

Rails compatibility constants

Constructors

new(model_class : T.class)
Source

Class methods

from_cached(model_class : T.class, cached_records : Array(T))

Initialize with cached records (for has_many associations)

Source

Instance methods

add_order_clause(clause : String)

Helper method to add order clause

Source
any?
Source
associated(association_name : String | Symbol)
Source
average(column : String | Symbol)

Calculate the average of values in the specified column. Returns the average as a number (possibly a floating-point number). Returns nil if no records match the query or if all values are NULL.

Examples: Order.average("subtotal") # => 25.125 Order.where(status: "shipped").average("subtotal") # => 29.83

SQL: SELECT AVG(subtotal) FROM orders SQL: SELECT AVG(subtotal) FROM orders WHERE (orders.status = 'shipped')

Source
clear_distinct
Source
clear_group
Source
clear_having
Source
clear_limit
Source
clear_none
Source
clear_offset
Source
clear_order

Helper method to clear order clauses

Source
clear_select
Source
clear_where

Helper methods for clearing various query parts

Source
copy_eager_loads(eager_loads : Array(String))
Source
copy_includes(includes : Array(String))
Source
copy_preloads(preloads : Array(String))
Source
count(column : String | Symbol) : Int64 | Hash(String, Int64)

Count records by a specific column, counting only non-null values. This is useful for counting records that have a value present in a specific field.

Examples: Customer.count(:title) # => 3 (only customers with a title) Customer.where(active: true).count(:email) # => 5 (active customers with email)

SQL: SELECT COUNT(title) FROM customers SQL: SELECT COUNT(email) FROM customers WHERE (customers.active = 1)

Source
count

Count records in the current relation. Returns the number of records matching the current query conditions.

Examples: Customer.count # => 5 Customer.where(first_name: 'Ryan').count # => 2 Customer.group(:status).count # => {"active" => 3, "inactive" => 2}

SQL: SELECT COUNT() FROM customers SQL: SELECT COUNT() FROM customers WHERE (customers.first_name = 'Ryan')

Source
create_with(attributes : Hash(String, DB::Any))

Set default attributes that will be used when creating new records. These attributes are only applied when creating new records, not when finding existing ones.

Examples: Customer.create_with(locked: false).find_or_create_by(first_name: "Andy") User.create_with(active: true, role: "user").find_or_create_by(email: "test@example.com")

The create_with attributes are merged with the find_or_create_by conditions when creating.

Source
create_with
Source
delete_all
Source
delete_by(conditions : Hash(String, DB::Any))
Source
delete_by
Source
destroy_all
Source
destroy_by(conditions : Hash(String, DB::Any))
Source
destroy_by
Source
distinct(value : Bool = true)
Source
dup

Create a deep copy of the query builder

Source
each
Source
eager_load(association_names : Array(String | Symbol))
Source
eager_load(*association_names : String | Symbol)
Source
empty?
Source
exists?
Source
explain

Run EXPLAIN on the current relation to analyze query execution plan. EXPLAIN output varies for each database adapter.

Examples: Customer.where(id: 1).joins(:orders).explain Customer.where(id: 1).includes(:orders).explain User.where("age > 21").explain(:analyze, :verbose) # PostgreSQL options Order.joins(:customer).explain(:analyze) # MySQL/MariaDB options

For databases that support it (PostgreSQL, MySQL, MariaDB), you can pass options: :analyze - Actually execute the query and show real execution statistics :verbose - Show additional details about the query plan :buffers - Show buffer usage information (PostgreSQL) :costs - Show cost estimates (PostgreSQL, default: true) :format - Output format: :text, :json, :xml, :yaml (PostgreSQL)

The method executes the query when using includes() since eager loading may trigger multiple queries, and some queries need results from previous ones.

Source
explain(*options : Symbol)
Source
find
Source
find_each(start : DB::Any | Nil = nil, finish : DB::Any | Nil = nil, batch_size : Int32 = 1000, error_on_ignore : Bool | Nil = nil, cursor : String | Array(String) = @model_class.primary_key, order : Symbol | Array(Symbol) = DEFAULT_ORDER, &block : T -> )

Retrieve records in batches and yield each one to the block. This is efficient for processing large datasets without loading everything into memory. Uses cursor-based batching for better performance than OFFSET-based batching.

Examples: User.where(active: true).find_each do |user| process_user(user) end

User.find_each(start: 2000, batch_size: 5000) do |user| UserMailer.newsletter(user).deliver_now end

Composite key ordering

Order.find_each(order: [:asc, :desc]) do |order| process_order(order) end

Options: :start - Starting cursor value (inclusive) :finish - Ending cursor value (inclusive) :batch_size - Number of records per batch (default: 1000) :error_on_ignore - Raise error if existing order is present (default: nil) :cursor - Column(s) to use for batching (default: primary_key) :order - Cursor order (:asc/:desc or array like [:asc, :desc], default: :asc)

Source
find_each(start : DB::Any | Nil = nil, finish : DB::Any | Nil = nil, batch_size : Int32 = 1000, error_on_ignore : Bool | Nil = nil, cursor : String | Array(String) = @model_class.primary_key, order : Symbol | Array(Symbol) = DEFAULT_ORDER)

Returns an Enumerator when no block is given

Source
find_in_batches(start : DB::Any | Nil = nil, finish : DB::Any | Nil = nil, batch_size : Int32 = 1000, error_on_ignore : Bool | Nil = nil, cursor : String | Array(String) = @model_class.primary_key, order : Symbol | Array(Symbol) = DEFAULT_ORDER, &block : Array(T) -> )

Yields each batch of records as an array. Uses cursor-based batching for better performance.

Examples: User.find_in_batches do |batch| batch.each { |user| process_user(user) } end

User.find_in_batches(start: 1000, batch_size: 500) do |batch| batch.each { |user| user.update_status } end

Options: Same as find_each - start, finish, batch_size, error_on_ignore, cursor, order

Source
find_in_batches(start : DB::Any | Nil = nil, finish : DB::Any | Nil = nil, batch_size : Int32 = 1000, error_on_ignore : Bool | Nil = nil, cursor : String | Array(String) = @model_class.primary_key, order : Symbol | Array(Symbol) = DEFAULT_ORDER)

Returns an Enumerator when no block is given

Source
find_or_create_by(conditions : Hash(String, DB::Any), &block : T -> )

Find the first record matching the current query conditions or create a new one. Uses any create_with attributes when creating new records.

Examples: User.where(active: true).find_or_create_by(name: "Andy") User.create_with(role: "user").find_or_create_by(email: "test@example.com")

Source
find_or_create_by(conditions : Hash(String, DB::Any))
Source
find_or_create_by
Source
find_or_create_by
Source
find_or_create_by!(conditions : Hash(String, DB::Any), &block : T -> )
Source
find_or_create_by!(conditions : Hash(String, DB::Any))
Source
find_or_create_by!
Source
find_or_create_by!
Source
find_or_initialize_by(conditions : Hash(String, DB::Any), &block : T -> )
Source
find_or_initialize_by(conditions : Hash(String, DB::Any))
Source
find_or_initialize_by
Source
find_or_initialize_by
Source
first(limit_count : Int32)

Find up to the specified number of records with ordering preserved from query.

Source
first
Source
first!(limit_count : Int32)

Find up to the specified number of records with ordering. Raises RecordNotFound if no records found.

Source
first!
Source
group(columns : Array(String))
Source
group(columns : Array(Symbol))
Source
group(*columns : String)
Source
group(*columns : Symbol)
Source
having(column : String, value : DB::Any)
Source
having(condition : String)
Source
having(condition : String, *params : DB::Any)
Source
ids

Pluck all the IDs for the relation using the table's primary key. This is a convenience method equivalent to pluck(primary_key).

Examples: Customer.ids # => [1, 2, 3] Customer.where(active: true).ids # => [1, 3]

Source
in_batches(of batch_size : Int32 = 1000, start : DB::Any | Nil = nil, finish : DB::Any | Nil = nil, load : Bool = false, error_on_ignore : Bool | Nil = nil, cursor : String | Array(String) = @model_class.primary_key, order : Symbol | Array(Symbol) = DEFAULT_ORDER, use_ranges : Bool | Nil = nil, &block : QueryBuilder(T) -> )

Yields QueryBuilder objects to work with a batch of records. This is similar to Rails' in_batches method.

Examples: User.where("age > 21").in_batches do |relation| relation.delete_all sleep(1) # Throttle the delete queries end

User.in_batches.each_with_index do |relation, batch_index| puts "Processing relation ##{batch_index}" relation.delete_all end

Options: :of - Specifies the size of the batch (default: 1000) :load - Specifies if the relation should be loaded (default: false) :start - Starting cursor value (inclusive) :finish - Ending cursor value (inclusive) :error_on_ignore - Raise error if existing order is present (default: nil) :cursor - Column(s) to use for batching (default: primary_key) :order - Cursor order (:asc/:desc or array, default: :asc) :use_ranges - Use range iteration for better performance (default: nil, auto-detected)

Source
in_batches(of batch_size : Int32 = 1000, start : DB::Any | Nil = nil, finish : DB::Any | Nil = nil, load : Bool = false, error_on_ignore : Bool | Nil = nil, cursor : String | Array(String) = @model_class.primary_key, order : Symbol | Array(Symbol) = DEFAULT_ORDER, use_ranges : Bool | Nil = nil)

Returns a BatchEnumerator when no block is given

Source
includes(association_names : Array(String | Symbol))
Source
includes(*association_names : String | Symbol)
Source
inner_join(table : String, on : String)
Source
inner_join(association_name : String | Symbol)
Source
joins(table : String, on : String)
Source
joins(associations : Array(String | Symbol))
Source
joins(nested_associations : Hash)

Simple flexible signature that can handle any nested structure

Source
joins(custom_sql : String)
Source
joins(*associations : String | Symbol)

Rails-compatible joins method that supports multiple associations and nested joins Examples: User.joins(:posts) # Single association User.joins(:posts, :account) # Multiple associations User.joins(posts: [:comments]) # Nested joins User.joins("LEFT JOIN bookmarks ON ...") # Custom SQL Author.joins(books: [{ reviews: { customer: :orders } }, :supplier]) # Complex nested

Source
joins

Support for NamedTuple syntax: User.joins(posts: [:comments])

Source
last(limit_count : Int32)

Find up to the specified number of records in reverse order.

Source
last
Source
last!(limit_count : Int32)

Find up to the specified number of records in reverse order. Raises RecordNotFound if no records found.

Source
last!
Source
left_join(table : String, on : String)
Source
left_join(association_name : String | Symbol)
Source
limit(count : Int32)
Source
lock(lock_type : String = "FOR UPDATE")

Adds a locking clause to the query for pessimistic locking. This is useful for preventing race conditions when updating records.

Examples: User.lock.first # SELECT * FROM users LIMIT 1 FOR UPDATE User.lock("LOCK IN SHARE MODE").first # SELECT * FROM users LIMIT 1 LOCK IN SHARE MODE User.where(active: true).lock.to_a # SELECT * FROM users WHERE active = 1 FOR UPDATE

The lock is automatically released when the transaction completes. It's recommended to wrap locked queries in a transaction:

User.transaction do user = User.lock.first user.update(name: "New Name") end

Source
many?

Check if there are more than one record matching the current query. Uses an optimized approach with LIMIT 2 to avoid counting all records.

Examples: Order.many? # => true if there are 2 or more orders Order.shipped.many? # => true if there are 2 or more shipped orders Book.where(out_of_print: true).many? # => true if there are 2 or more out of print books

Source
maximum(column : String | Symbol)

Find the maximum value in the specified column. Returns the maximum value with the corresponding data type. Returns nil if no records match the query or if all values are NULL.

Examples: Order.maximum("subtotal") # => 199.99 Order.where(status: "shipped").maximum("created_at") # => 2023-12-31 23:59:59

SQL: SELECT MAX(subtotal) FROM orders SQL: SELECT MAX(created_at) FROM orders WHERE (orders.status = 'shipped')

Source
merge(other_relation : QueryBuilder(T))

Merge another relation's conditions, replacing existing ones where they conflict This is useful for overriding conditions from scopes or previous where clauses

Examples: Book.in_print.merge(Book.out_of_print) # out_of_print condition wins User.where(active: true).merge(User.where(active: false)) # active: false wins

The merge method replaces conflicting conditions rather than adding them with AND

Source
minimum(column : String | Symbol)

Find the minimum value in the specified column. Returns the minimum value with the corresponding data type. Returns nil if no records match the query or if all values are NULL.

Examples: Order.minimum("subtotal") # => 5.99 Order.where(status: "shipped").minimum("created_at") # => 2023-01-15 10:30:00

SQL: SELECT MIN(subtotal) FROM orders SQL: SELECT MIN(created_at) FROM orders WHERE (orders.status = 'shipped')

Source
missing(association_name : String | Symbol)
Source
none

Return an empty relation that fires no queries

Source
not(column : String, values : Array(Int32))
Source
not(column : String, values : Array(Int64))
Source
not(column : String, values : Array(String))
Source
not(column : String, values : Array(Float32))
Source
not(column : String, values : Array(Float64))
Source
not(column : String, values : Array(Bool))
Source
not(column : String, values : Array(Time))
Source
not(column : String, values : Array(DB::Any))
Source
not(column_with_operator : String, value : Int32)
Source
not(column_with_operator : String, value : Int64)
Source
not(column_with_operator : String, value : String)
Source
not(column_with_operator : String, value : Float32)
Source
not(column_with_operator : String, value : Float64)
Source
not(column_with_operator : String, value : Bool)
Source
not(column_with_operator : String, value : Time)
Source
not(column_with_operator : String, value : DB::Any)
Source
not(column : String, range : Range(Int32, Int32))
Source
not(column : String, range : Range(Int64, Int64))
Source
not(column : String, range : Range(Float32, Float32))
Source
not(column : String, range : Range(Float64, Float64))
Source
not(column : String, range : Range(Time, Time))
Source
not(column : String, range : Range(String, String))
Source
not(conditions : Hash(String, DB::Any))
Source
not(condition : String, *params : Int32)
Source
not(condition : String, *params : Int64)
Source
not(condition : String, *params : String)
Source
not(condition : String, *params : Float32)
Source
not(condition : String, *params : Float64)
Source
not(condition : String, *params : Bool)
Source
not(condition : String, *params : Time)
Source
not(condition : String, *params : DB::Any)
Source
offset(count : Int32)
Source
only(*clauses : Symbol)

Keep only specified clauses

Source
or(column_with_operator : String, value : Int32)
Source
or(column_with_operator : String, value : Int64)
Source
or(column_with_operator : String, value : String)
Source
or(column_with_operator : String, value : Float32)
Source
or(column_with_operator : String, value : Float64)
Source
or(column_with_operator : String, value : Bool)
Source
or(column_with_operator : String, value : Time)
Source
or(column_with_operator : String, value : DB::Any)
Source
or(column : String, values : Array(Int32))
Source
or(column : String, values : Array(Int64))
Source
or(column : String, values : Array(String))
Source
or(column : String, values : Array(Float32))
Source
or(column : String, values : Array(Float64))
Source
or(column : String, values : Array(Bool))
Source
or(column : String, values : Array(Time))
Source
or(column : String, values : Array(DB::Any))
Source
or(column : String, range : Range(Int32, Int32))
Source
or(column : String, range : Range(Int64, Int64))
Source
or(column : String, range : Range(Float32, Float32))
Source
or(column : String, range : Range(Float64, Float64))
Source
or(column : String, range : Range(Time, Time))
Source
or(column : String, range : Range(String, String))
Source
or(conditions : Hash(String, DB::Any))
Source
or(condition : String, *params : Int32)
Source
or(condition : String, *params : Int64)
Source
or(condition : String, *params : String)
Source
or(condition : String, *params : Float32)
Source
or(condition : String, *params : Float64)
Source
or(condition : String, *params : Bool)
Source
or(condition : String, *params : Time)
Source
or(condition : String, *params : DB::Any)
Source
order(column : Symbol)

Order with symbol column (single column, defaults to ASC)

Source
order(column : String, direction : String = "ASC")

Order with string column and optional direction

Source
order(order_hash : Hash(Symbol | String, Hash(Symbol | String, Symbol | String) | Symbol | String))

Order with hash parameter (for nested table ordering like books: { print_year: :desc })

Source
order(columns : Array(String))

Order with array of strings (for multiple string arguments)

Source
order(first_column : Symbol, **additional_columns)

Order with mixed arguments (symbol + hash) - special case

Source
order

Order with hash of columns and directions (named parameters)

Source
order_by(column : String, direction : String = "ASC")
Source
order_clauses

Getter methods for debugging

Source
page(page_number : Int32, per_page : Int32 = 20)
Source
params

Expose query parameters for testing and debugging

Source
pick(column : String)

Pick the value(s) from the named column(s) in the current relation. Returns the first row of the specified column values with corresponding data type. This is a short-hand for relation.limit(1).pluck(*column_names).first.

Examples: Customer.where(id: 1).pick(:id) # => 1 Customer.where(id: 1).pick(:id, :first_name) # => [1, "David"] Customer.where(id: 999).pick(:id) # => nil

Source
pick(*columns : String)
Source
pluck(column : String)
Source
pluck(*columns : String)
Source
preload(association_names : Array(String | Symbol))
Source
preload(*association_names : String | Symbol)
Source
readonly

Return a relation that marks all returned records as readonly

Source
regroup(columns : Array(String))
Source
regroup(columns : Array(Symbol))
Source
regroup(*columns : String)

Replace existing group clause

Source
regroup(*columns : Symbol)
Source
reject
Source
reorder(column : String, direction : String = "ASC")
Source
reorder(*columns : String)

Override existing order clause

Source
reorder(*columns : Symbol)
Source
reorder
Source
reselect(columns : Array(String))
Source
reselect(columns : Array(Symbol))
Source
reselect(*columns : String)

Override existing select clause

Source
reselect(*columns : Symbol)
Source
reverse_order

Create a new query with reversed ordering

Source
rewhere(conditions : Hash(String, DB::Any))

Replace existing where conditions

Source
rewhere(condition : String, *params : DB::Any)
Source
rewhere
Source
right_join(table : String, on : String)
Source
right_join(association_name : String | Symbol)
Source
select(columns : Array(String))
Source
select(columns : Array(Symbol))
Source
select(*columns : String)
Source
select(*columns : Symbol)
Source
select
Source
select_all(sql : String, params : Array(DB::Any) = [] of DB::Any)

Execute a custom SQL query and return raw results as an array of hashes. This is similar to find_by_sql but returns raw data instead of model instances. This method is equivalent to Rails' lease_connection.select_all.

Examples: Customer.select_all("SELECT first_name, created_at FROM customers WHERE id = '1'")

=> [{"first_name" => "Rafael", "created_at" => "2012-11-10 23:23:45.281189"}]

The method always returns an array of hashes, even if the query returns a single record. Returns an empty array if no records are found.

Source
select_all(sql : String, *params : DB::Any)
Source
set_create_with_attributes(attributes : Hash(String, DB::Any))
Source
set_distinct(value : Bool)
Source
set_group(clause : String | Nil)
Source
set_having_conditions(conditions : Array(String), params : Array(DB::Any))
Source
set_joins(joins : Array(String), has_joins : Bool)
Source
set_limit(value : Int32 | Nil)
Source
set_lock(lock_clause : String | Nil)
Source
set_none(none : Bool)
Source
set_offset(value : Int32 | Nil)
Source
set_order_clauses(clauses : Array(String))
Source
set_readonly(readonly : Bool)
Source
set_select(clause : String | Nil)
Source
set_strict_loading(strict_loading : Bool)
Source
set_where_conditions(conditions : Array(String), params : Array(DB::Any))
Source
strict_loading

Return a relation that enables strict loading to prevent N+1 queries When strict loading is enabled, accessing associations that weren't preloaded will raise an error

Source
sum(column : String | Symbol)

Calculate the sum of values in the specified column. Returns the sum as a number (possibly a floating-point number). Returns 0 if no records match the query or if all values are NULL.

Examples: Order.sum("subtotal") # => 150.75 Order.where(status: "shipped").sum("subtotal") # => 89.50

SQL: SELECT SUM(subtotal) FROM orders SQL: SELECT SUM(subtotal) FROM orders WHERE (orders.status = 'shipped')

Source
take(limit_count : Int32)

Retrieve up to the specified number of records without any implicit ordering.

Source
take

Retrieve a record without any implicit ordering. Returns nil if no record found.

Source
take!(limit_count : Int32)

Retrieve up to the specified number of records without any implicit ordering. Raises RecordNotFound if no records found.

Source
take!

Retrieve a record without any implicit ordering. Raises RecordNotFound if no record found.

Source
test_method

Test method to check if methods are being found

Source
to_a
Source
to_sql
Source
unscope(*clauses : Symbol)

Remove specific conditions from query

Source
unscope(*, where clause_name : Symbol)

Remove specific where conditions

Source
unscoped

Remove all scoping and return a fresh query builder This is useful for bypassing default scopes or removing all existing conditions

Examples: User.where(active: true).unscoped.all # Removes the where condition Book.unscoped.load # Bypasses any default scope

This method removes all scoping and will do a normal query on the table

Source
update_all(attributes : Hash(String, DB::Any))
Source
update_all
Source
where(column_with_operator : String, value : Int32)
Source
where(column_with_operator : String, value : Int64)
Source
where(column_with_operator : String, value : String)
Source
where(column_with_operator : String, value : Float32)
Source
where(column_with_operator : String, value : Float64)
Source
where(column_with_operator : String, value : Bool)
Source
where(column_with_operator : String, value : Time)
Source
where(column_with_operator : String, value : DB::Any)
Source
where(column : String, values : Array(Int32))
Source
where(column : String, values : Array(Int64))
Source
where(column : String, values : Array(String))
Source
where(column : String, values : Array(Float32))
Source
where(column : String, values : Array(Float64))
Source
where(column : String, values : Array(Bool))
Source
where(column : String, values : Array(Time))
Source
where(column : String, values : Array(DB::Any))
Source
where(condition : String, named_params : Hash(String, DB::Any))

Support for named placeholder conditions like: User.where("name = :name AND age > :min_age", {name: "John", min_age: 18}) User.where("created_at >= :start_date AND created_at <= :end_date", {start_date: params[:start_date], end_date: params[:end_date]})

Source
where(condition : String, named_params : NamedTuple)

Support for NamedTuple syntax: User.where("name = :name", {name: "Alice"})

Source
where(column : String, range : Range(Int32, Int32))
Source
where(column : String, range : Range(Int64, Int64))
Source
where(column : String, range : Range(Float32, Float32))
Source
where(column : String, range : Range(Float64, Float64))
Source
where(column : String, range : Range(Time, Time))
Source
where(column : String, range : Range(String, String))
Source
where(conditions : Hash(String, DB::Any))
Source
where(condition : String, *params : Int32)
Source
where(condition : String, *params : Int64)
Source
where(condition : String, *params : String)
Source
where(condition : String, *params : Float32)
Source
where(condition : String, *params : Float64)
Source
where(condition : String, *params : Bool)
Source
where(condition : String, *params : Time)
Source
where(condition : String, *params : DB::Any)
Source
where(condition : String, **named_params)

Named placeholder conditions - must come before variadic method for proper resolution

Source
where
Source
where_conditions
Source

Macros

generate_where_overloads
Source
method_missing(call)

Runtime method_missing for dynamic scope delegation

Source