Lustra::Model::CollectionBase(T)
Inherits Lustra::SQL::SelectBuilder < Lustra::SQL::Query::WithPagination < Lustra::SQL::Query::BeforeQuery < Lustra::SQL::Query::Change < Lustra::SQL::Query::Connection < Lustra::SQL::Query::Pluck < Lustra::SQL::Query::Fetch < Lustra::SQL::Query::Execute < Lustra::SQL::Query::Lock < Lustra::SQL::Query::Window < Lustra::SQL::Query::CTE < Lustra::SQL::Query::Aggregate < Lustra::SQL::Query::OffsetLimit < Lustra::SQL::Query::GroupBy < Lustra::SQL::Query::OrderBy < Lustra::SQL::Query::Having < Lustra::SQL::Query::Where < Lustra::SQL::Query::Join < Lustra::SQL::Query::From < Lustra::SQL::Query::Select < Enumerable < Reference < Object
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.
Instance methods
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)
Find multiple models by an array of primary keys. Returns an array of models (may be empty if none found).
A convenient way to write where { condition }.first!(fetch_columns)
A convenient way to write where({any_column: "any_value"}).first!(fetch_columns)
Find multiple models by an array of primary keys. Raises error if ANY of the IDs are not found.
A convenient way to write where { condition }.first(fetch_columns)
Find a model by column values. Returns nil if not found.
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.
user = User.query.find_by(email: "test@example.com")
user = User.query.where { active == true }.find_by(role: "admin")
A convenient way to write where { condition }.first!(fetch_columns)
Find a model by column values. Raises error if not found.
user = User.query.find_by!(email: "test@example.com")
Find a model by column values. Raises error if not found.
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.
Return an empty, chainable collection (Rails-like .none).
Useful for conditional branches where no records should be returned
while keeping query chaining intact.
User.query.none.where { active == true }.count # => 0
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.