class
Lustra::Model::CollectionBase(T)
- Lustra::Model::CollectionBase(T)
- Reference
- Object
Overview
CollectionBase(T)
is the base class for collection of model.
Collection of model are a SQL SELECT
query mapping & building system. They are Enumerable and are
`Lustra::SQL::SelectBuilder` behavior; therefore, they can be used array-like and are working with low-level SQL
Building.
The CollectionBase(T)
is extended by each model. For example, generating the model MyModel
will generate the
class MyModel::Collection
which inherits from CollectionBase(MyModel)
Collection are instantiated using Model.query
method.
Included Modules
- Enumerable(T)
- Lustra::SQL::SelectBuilder
Direct Known Subclasses
Defined in:
lustra/model/collection.crInstance Method Summary
-
#<<(item : T)
Add an item to the current collection.
-
#[](range : Range(Number, Number), fetch_columns = false) : Array(T)
Get a range of models
-
#[](off, fetch_columns = false) : T
Basically a fancy way to write
OFFSET x LIMIT 1
-
#[]?(off, fetch_columns = false) : T | Nil
Basically a fancy way to write
OFFSET x LIMIT 1
-
#add(item : T)
Alias for
Collection#<<
-
#any?
Check whether the query return any row.
- #association_name : String | Nil
- #association_name=(association_name : String | Nil)
- #autosave=(autosave : Bool)
- #autosave? : Bool
-
#build(x : NamedTuple, &block : T -> Nil) : T
Build a new collection; if the collection comes from a has_many relation (e.g.
-
#build(**tuple, & : T -> Nil) : T
Build a new collection; if the collection comes from a has_many relation (e.g.
-
#build(x : NamedTuple) : T
Build a new collection; if the collection comes from a has_many relation (e.g.
-
#build(**tuple) : T
Build a new collection; if the collection comes from a has_many relation (e.g.
-
#count(type : X.class = Int64) forall X
Use SQL
COUNT
over your query, and return this number as a Int64 -
#create(x : NamedTuple, &block : T -> Nil) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#create(**tuple, & : T -> Nil) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#create(x : NamedTuple) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#create(**tuple) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#create!(x : NamedTuple, &block : T -> Nil) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#create!(**tuple, & : T -> Nil) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#create!(x : NamedTuple) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#create!(**tuple) : T
Build a new object and setup the fields like setup in the condition tuple.
-
#delete_all : self
Delete all the rows which would have been returned by this collection WITHOUT callbacks.
-
#destroy_all : self
Destroy all the rows which would have been returned by this collection WITH callbacks.
-
#dup
Duplicate the query
-
#each(fetch_columns = false, & : T -> ) : Nil
Build the SQL, send the query then iterate through each models gathered by the request.
-
#each_with_cursor(batch = 1000, fetch_columns = false, &block : T -> )
Build the SQL, send the query then iterate through each models gathered by the request.
-
#empty?
Inverse of
#any?
, return true if the request return no rows. -
#find(fetch_columns = false, &) : T | Nil
A convenient way to write
where { condition }.first(fetch_columns)
-
#find(tuple : NamedTuple, fetch_columns = false) : T | Nil
A convenient way to write
where({any_column: "any_value"}).first(fetch_columns)
-
#find(**tuple) : T | Nil
A convenient way to write
where({any_column: "any_value"}).first
-
#find!(fetch_columns = false, &) : T
A convenient way to write
where { condition }.first!(fetch_columns)
-
#find!(tuple : NamedTuple, fetch_columns = false) : T
A convenient way to write
where({any_column: "any_value"}).first!(fetch_columns)
-
#find!(**tuple) : T
A convenient way to write
where({any_column: "any_value"}).first!
-
#find_by(tuple : NamedTuple, fetch_columns = false) : T | Nil
Find a model by column values.
-
#find_by(**tuple) : T | Nil
Find a model by column values.
-
#find_by!(tuple : NamedTuple, fetch_columns = false) : T
Find a model by column values.
-
#find_by!(**tuple) : T
Find a model by column values.
- #find_or_build(x : NamedTuple, &block : T -> Nil) : T
-
#find_or_build(**tuple, & : T -> Nil) : T
Try to fetch a row.
- #find_or_build(x : NamedTuple) : T
- #find_or_build(**tuple) : T
-
#find_or_create(x : NamedTuple, &block : T -> Nil) : T
Try to fetch a row.
-
#find_or_create(**tuple, & : T -> Nil) : T
Try to fetch a row.
-
#find_or_create(x : NamedTuple) : T
Try to fetch a row.
-
#find_or_create(**tuple) : T
Try to fetch a row.
-
#first(fetch_columns = false) : T | Nil
Get the first row from the collection query.
-
#first!(fetch_columns = false) : T
Get the first row from the collection query.
-
#full_outer_join(association : Lustra::SQL::Symbolic, lateral = false)
FULL_OUTER JOIN using association name (auto-detects join conditions)
-
#ids : Array(Lustra::SQL::Any)
Convenient shortcut to get an array of primary key values.
-
#inner_join(association : Lustra::SQL::Symbolic, lateral = false)
INNER JOIN using association name (auto-detects join conditions)
-
#item_class
Return the model class for this collection
-
#join(association : Lustra::SQL::Symbolic, type = :inner, lateral = false)
Join a relation using association name (auto-detects join conditions) Overrides the parent join to handle association names without blocks
-
#last(fetch_columns = false) : T | Nil
Get the last row from the collection query.
-
#last!(fetch_columns = false) : T
Get the last row from the collection query.
-
#left_join(association : Lustra::SQL::Symbolic, lateral = false)
LEFT JOIN using association name (auto-detects join conditions)
-
#map(fetch_columns = false, &block : T -> X) : Array(X) forall X
Build the SQL, send the query then build and array by applying the block transformation over it.
-
#parent_model : Lustra::Model | Nil
Parent model context for autosave functionality
-
#parent_model=(parent_model : Lustra::Model | Nil)
Parent model context for autosave functionality
-
#right_join(association : Lustra::SQL::Symbolic, lateral = false)
RIGHT JOIN using association name (auto-detects join conditions)
-
#save!(item : T)
Save a model and handle append_operation for has_many through relationships This allows the build + save pattern to work
- #tags
-
#to_a(fetch_columns = false) : Array(T)
Create an array from the query.
-
#unlink(item : T)
Unlink the model currently referenced through a relation
has_many through
-
#update_all(fields : NamedTuple) : Int64
Update all the rows which would have been returned by this collection without loading the models into memory.
-
#update_all(fields : Hash(String, Lustra::SQL::Any)) : Int64
Update all the rows which would have been returned by this collection without loading the models into memory.
-
#update_all(**fields) : Int64
Update all the rows which would have been returned by this collection without loading the models into memory.
Instance methods inherited from module Lustra::SQL::SelectBuilder
before_query(&block : -> Nil)
before_query,
columns : Array(SQL::Column)
columns,
default_wildcard_table
default_wildcard_table,
dup : self
dup,
havings : Array(Lustra::Expression::Node)
havings,
is_distinct?
is_distinct?,
joins : Array(SQL::Join)
joins,
limit : Int64 | Nil
limit,
lock : String | Nil
lock,
offset : Int64 | Nil
offset,
order_bys : Array(Lustra::SQL::Query::OrderBy::Record)
order_bys,
to_delete
to_delete,
to_sql : String
to_sql,
to_update
to_update,
total_entries : Int64 | Nil
total_entries,
total_entries=(total_entries : Int64 | Nil)
total_entries=,
wheres : Array(Lustra::Expression::Node)
wheres
Constructor methods inherited from module Lustra::SQL::SelectBuilder
new(distinct_value = nil, cte = {} of String => Lustra::SQL::SelectBuilder | String, columns = [] of SQL::Column, froms = [] of SQL::From, joins = [] of SQL::Join, wheres = [] of Lustra::Expression::Node, havings = [] of Lustra::Expression::Node, windows = [] of ::Tuple(String, String), group_bys = [] of Symbolic, order_bys = [] of Lustra::SQL::Query::OrderBy::Record, limit = nil, offset = nil, lock = nil, before_query_triggers = [] of (-> Nil))
new
Instance methods inherited from module Lustra::SQL::Query::WithPagination
current_page : Int32 | Int64
current_page,
first_page?
first_page?,
last_page?
last_page?,
next_page
next_page,
out_of_bounds?
out_of_bounds?,
paginate(page : Int32 = DEFAULT_PAGE, per_page : Int32 = DEFAULT_LIMIT)
paginate,
per_page : Int32 | Int64
per_page,
previous_page
previous_page,
total_pages : Int32 | Int64
total_pages
Instance methods inherited from module Lustra::SQL::Query::Change
change! : self
change!
Instance methods inherited from module Lustra::SQL::Query::Connection
connection_name : String
connection_name,
use_connection(connection_name : String)
use_connection
Instance methods inherited from module Lustra::SQL::Query::Pluck
pluck(fields : Tuple(*T)) forall Tpluck(*fields) : Array
pluck(**fields : **T) forall T pluck, pluck_col(field : Lustra::SQL::Symbolic, type : T.class) : Array(T) forall T
pluck_col(field : Lustra::SQL::Symbolic) : Array(Lustra::SQL::Any) pluck_col
Instance methods inherited from module Lustra::SQL::Query::Fetch
fetch(fetch_all = false, & : Hash(String, Lustra::SQL::Any) -> Nil)
fetch,
fetch_first
fetch_first,
fetch_first!
fetch_first!,
fetch_with_cursor(count = 1000, & : Hash(String, Lustra::SQL::Any) -> Nil)
fetch_with_cursor,
first
first,
first!
first!,
scalar(type : T.class) forall T
scalar,
to_a : Array(Hash(String, Lustra::SQL::Any))
to_a
Instance methods inherited from module Lustra::SQL::Query::Execute
execute(connection_name : String | Nil = nil)
execute,
execute_and_count(connection_name : String | Nil = nil) : Int64
execute_and_count,
explain(connection_name : String | Nil = nil) : String
explain,
explain_analyze(connection_name : String | Nil = nil) : String
explain_analyze
Instance methods inherited from module Lustra::SQL::Query::Lock
with_lock(str : String = "FOR UPDATE")
with_lock
Instance methods inherited from module Lustra::SQL::Query::Window
clear_windows
clear_windows,
print_windows
print_windows,
window(name, value)window(windows : NamedTuple) window, windows : Array(WindowDeclaration) windows
Instance methods inherited from module Lustra::SQL::Query::CTE
cte : Hash(String, CTEAuthorized)
cte,
with_cte(name, request : CTEAuthorized)with_cte(tuple : NamedTuple) with_cte
Instance methods inherited from module Lustra::SQL::Query::Aggregate
agg(field, x : X.class) forall X
agg,
avg(field, x : X.class) forall X
avg,
count(type : X.class = Int64) forall X
count,
exists? : Bool
exists?,
max(field, x : X.class) forall X
max,
min(field, x : X.class) forall X
min,
sum(field) : Float64
sum
Instance methods inherited from module Lustra::SQL::Query::OffsetLimit
clear_limit
clear_limit,
clear_offset
clear_offset,
limit(x : Int | Nil)
limit,
offset(x : Int | Nil)
offset
Instance methods inherited from module Lustra::SQL::Query::GroupBy
clear_group_bys
clear_group_bys,
group_by(column : Symbolic)
group_by,
group_bys : Array(Symbolic)
group_bys
Instance methods inherited from module Lustra::SQL::Query::OrderBy
clear_order_bys
clear_order_bys,
order_by(tuple : NamedTuple)order_by(expression : Symbol, direction : Symbol = :asc, nulls : Symbol | Nil = nil)
order_by(expression : String, direction : Symbol = :asc, nulls : Symbol | Nil = nil)
order_by(**tuple) order_by, reverse_order_by reverse_order_by
Instance methods inherited from module Lustra::SQL::Query::Having
clear_havings
clear_havings,
having(node : Lustra::Expression::Node)having(&)
having(conditions : NamedTuple | Hash(String, Lustra::SQL::Any))
having(template : String, *args)
having(template : String, **tuple)
having(**tuple) having, or_having(node : Lustra::Expression::Node)
or_having(template : String, *args)
or_having(template : String, **named_tuple)
or_having(&) or_having
Instance methods inherited from module Lustra::SQL::Query::Where
clear_wheres
clear_wheres,
not(&)not(conditions : NamedTuple | Hash(String, Lustra::SQL::Any))
not(template : String)
not(template : String, *args)
not(template : String, **tuple)
not(**tuple) not, or(node : Lustra::Expression::Node)
or(&)
or(conditions : NamedTuple | Hash(String, Lustra::SQL::Any))
or(template : String, *args)
or(template : String, **tuple)
or(**tuple) or, where(node : Lustra::Expression::Node)
where(&)
where(conditions : NamedTuple | Hash(String, Lustra::SQL::Any))
where(template : String)
where(template : String, *args)
where(template : String, **tuple)
where(**tuple) where
Instance methods inherited from module Lustra::SQL::Query::Join
cross_join(name : Selectable, lateral = false)
cross_join,
full_outer_join(name : Selectable, lateral = false, &)full_outer_join(name : Selectable, condition : String = "true", lateral = false) full_outer_join, inner_join(name : Selectable, lateral = false, &)
inner_join(name : Selectable, condition : String = "true", lateral = false) inner_join, join(name : Selectable, type = :inner, lateral = false, &)
join(name : Selectable, type = :inner, condition : String = "true", lateral = false)
join(name : Selectable, type = :inner, lateral = false) join, left_join(name : Selectable, lateral = false, &)
left_join(name : Selectable, condition : String = "true", lateral = false) left_join, right_join(name : Selectable, lateral = false, &)
right_join(name : Selectable, condition : String = "true", lateral = false) right_join
Instance methods inherited from module Lustra::SQL::Query::From
clear_from
clear_from,
from(*args)from(**tuple) from, froms : Array(SQL::From) froms
Instance methods inherited from module Lustra::SQL::Query::Select
clear_distinct
clear_distinct,
clear_select
clear_select,
default_wildcard_table=(table : String | Nil = nil)
default_wildcard_table=,
distinct(on : String | Nil = "")
distinct,
distinct_value : String | Nil
distinct_value,
select(c : Column)select(*args) select
Instance Method Detail
Add an item to the current collection.
If the current collection is not originated from a has_many
or has_many through:
relation, calling <<
over
the collection will raise a Lustra::SQL::OperationNotPermittedError
Returns self
and therefore can be chained
Build a new collection; if the collection comes from a has_many relation
(e.g. my_model.associations.build
), the foreign column which store
the primary key of my_model
will be setup by default, preventing you
to forget it.
You can pass extra parameters using a named tuple:
my_model.associations.build({a_column: "value"})
Build a new collection; if the collection comes from a has_many relation
(e.g. my_model.associations.build
), the foreign column which store
the primary key of my_model
will be setup by default, preventing you
to forget it.
You can pass extra parameters using a named tuple:
my_model.associations.build({a_column: "value"})
Build a new collection; if the collection comes from a has_many relation
(e.g. my_model.associations.build
), the foreign column which store
the primary key of my_model
will be setup by default, preventing you
to forget it.
You can pass extra parameters using a named tuple:
my_model.associations.build({a_column: "value"})
Build a new collection; if the collection comes from a has_many relation
(e.g. my_model.associations.build
), the foreign column which store
the primary key of my_model
will be setup by default, preventing you
to forget it.
You can pass extra parameters using a named tuple:
my_model.associations.build({a_column: "value"})
Use SQL COUNT
over your query, and return this number as a Int64
Build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Build a new object and setup
the fields like setup in the condition tuple.
Just after building, save the object.
But instead of returning self if validation failed,
raise Lustra::Model::InvalidError
exception
Build a new object and setup
the fields like setup in the condition tuple.
Just after building, save the object.
But instead of returning self if validation failed,
raise Lustra::Model::InvalidError
exception
Build a new object and setup
the fields like setup in the condition tuple.
Just after building, save the object.
But instead of returning self if validation failed,
raise Lustra::Model::InvalidError
exception
Build a new object and setup
the fields like setup in the condition tuple.
Just after building, save the object.
But instead of returning self if validation failed,
raise Lustra::Model::InvalidError
exception
Delete all the rows which would have been returned by this collection WITHOUT callbacks.
This is a bulk operation that doesn't load models into memory.
Is equivalent to collection.to_delete.execute
User.query.where { active == false }.delete_all
Returns self
for chaining.
Destroy all the rows which would have been returned by this collection WITH callbacks.
This loads each model into memory and calls destroy
on it, triggering all :delete
callbacks.
Use #delete_all
if you don't need callbacks (much faster for large datasets).
# With callbacks - slower but safe
User.query.where { active == false }.destroy_all
# Without callbacks - faster
User.query.where { active == false }.delete_all
Returns self
for chaining.
Build the SQL, send the query then iterate through each models gathered by the request.
Build the SQL, send the query then iterate through each models gathered by the request. Use a postgres cursor to avoid memory bloating. Useful to fetch millions of rows at once.
A convenient way to write where { condition }.first(fetch_columns)
A convenient way to write where({any_column: "any_value"}).first(fetch_columns)
A convenient way to write where { condition }.first!(fetch_columns)
A convenient way to write where({any_column: "any_value"}).first!(fetch_columns)
Find a model by column values. Returns nil
if not found.
This is an alias for #find(**tuple)
with better naming.
user = User.query.find_by(email: "test@example.com")
user = User.query.where { active == true }.find_by(role: "admin")
Find a model by column values. Returns nil
if not found.
This is an alias for #find(**tuple)
with better naming.
user = User.query.find_by(email: "test@example.com")
user = User.query.where { active == true }.find_by(role: "admin")
Find a model by column values. Raises error if not found.
This is an alias for #find!(**tuple)
with better naming.
user = User.query.find_by!(email: "test@example.com")
Find a model by column values. Raises error if not found.
This is an alias for #find!(**tuple)
with better naming.
user = User.query.find_by!(email: "test@example.com")
Try to fetch a row. If not found, build a new object and setup the fields like setup in the condition tuple.
Try to fetch a row. If not found, build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Try to fetch a row. If not found, build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Try to fetch a row. If not found, build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Try to fetch a row. If not found, build a new object and setup the fields like setup in the condition tuple. Just after building, save the object.
Get the first row from the collection query.
if not found, return nil
Get the first row from the collection query. if not found, throw an error
FULL_OUTER JOIN using association name (auto-detects join conditions)
Convenient shortcut to get an array of primary key values.
Equivalent to pluck_col(T.__pkey__)
but more readable.
User.query.where { active == true }.ids
# => [1, 2, 3, 4, 5]
Post.query.where { published == true }.ids
# => [10, 25, 42, 100]
Returns an array of primary key values (typically Array(Int64)
or Array(Int32)
).
INNER JOIN using association name (auto-detects join conditions)
Join a relation using association name (auto-detects join conditions) Overrides the parent join to handle association names without blocks
Get the last row from the collection query.
if not found, return nil
Get the last row from the collection query. if not found, throw an error
LEFT JOIN using association name (auto-detects join conditions)
Build the SQL, send the query then build and array by applying the block transformation over it.
Parent model context for autosave functionality
RIGHT JOIN using association name (auto-detects join conditions)
Save a model and handle append_operation for has_many through relationships This allows the build + save pattern to work
Unlink the model currently referenced through a relation has_many through
If the current colleciton doesn't come from a has_many through
relation,
this method will throw Lustra::SQL::OperationNotPermittedError
Returns true
if unlinking is successful (e.g. one or more rows have been updated), or false
otherwise
Update all the rows which would have been returned by this collection without loading the models into memory. Bypasses validations and callbacks.
This is useful for bulk updates where you don't need to run validations or callbacks on each individual model.
# Update all inactive users to have a specific status
affected = User.query.where { active == false }.update_all(status: "inactive")
puts "Updated #{affected} users"
# Update multiple columns at once
Post.query.where { published == false }.update_all(published: true, published_at: Time.utc)
# With complex conditions
User.query.where { created_at < 1.year.ago }.update_all(archived: true)
Returns the number of rows affected.
Update all the rows which would have been returned by this collection without loading the models into memory. Bypasses validations and callbacks.
This is useful for bulk updates where you don't need to run validations or callbacks on each individual model.
# Update all inactive users to have a specific status
affected = User.query.where { active == false }.update_all(status: "inactive")
puts "Updated #{affected} users"
# Update multiple columns at once
Post.query.where { published == false }.update_all(published: true, published_at: Time.utc)
# With complex conditions
User.query.where { created_at < 1.year.ago }.update_all(archived: true)
Returns the number of rows affected.
Update all the rows which would have been returned by this collection without loading the models into memory. Bypasses validations and callbacks.
This is useful for bulk updates where you don't need to run validations or callbacks on each individual model.
# Update all inactive users to have a specific status
affected = User.query.where { active == false }.update_all(status: "inactive")
puts "Updated #{affected} users"
# Update multiple columns at once
Post.query.where { published == false }.update_all(published: true, published_at: Time.utc)
# With complex conditions
User.query.where { created_at < 1.year.ago }.update_all(archived: true)
Returns the number of rows affected.