abstract struct Interro::QueryBuilder(T)

Overview

Defining QueryBuilder objects is a way to create composable queries. For example, if you have the following Model and QueryBuilder:

struct User
  include Interro::Model

  getter id : UUID
  getter name : String
  getter email : String
  getter team_id : UUID?
  getter role : Role

  enum Role
    Member
    TeamAdmin
    SiteAdmin
  end
end

struct UserQuery < Interro::QueryBuilder(User)
  table "users"

  def find(id : UUID)
    where(id: id).first?
  end

  def on_team(team : Team)
    where team_id: team.id
  end

  def with_role(role : User::Role)
    # We pass `role.value` to the SQL query since it's stored in `INTEGER`
    # column type.
    where role: role.value
  end

  def sorted_by_name(direction : Interro::QueryBuilder::OrderByDirection = :asc)
    order_by name: direction
  end
end

Then you can find all team admins like this:

users = UserQuery.new
  .on_team(team)
  .with_role(:team_admin)
  .sorted_by_name

You can insert records with the insert method:

struct UserQuery < Interro::QueryBuilder(User)
  # ...
  def create(name : String, email : String, team : Team, role : User::Role)
    # This generates the following SQL:
    #   INSERT INTO users (name, email, team_id, role) VALUES ($1, $2, $3, $4)
    # And it passes these args to the parameterized query:
    #   [name, email, team.id, role.value]
    insert(
      name: name,
      email: email,
      team_id: team.id,
      role: role.value,
    )
  end
end

You can also use Interro::Validations::Result objects to validate the inputs before saving them to the DB. Since Interro::QueryBuilder includes the Interro::Validations mixin, you can simply refer to it as Result.

struct UserQuery < Interro::QueryBuilder(User)
  # ...
  def create(name : String, email : String, team : Team, role : User::Role)
    Result(User).new
      .validate_presence(name: name, email: email)
      .validate_uniqueness("email") { where(email: email).any? }
      .valid do
        insert(
          name: name,
          email: email,
          team_id: team.id,
          role: role.value,
        )
      end
  end
end

Included Modules

Direct Known Subclasses

Defined in:

dynamic_query.cr
query_builder.cr

Constructors

Macro Summary

Instance Method Summary

Constructor Detail

def self.[](transaction : DB::Transaction) : self #

[View source]
def self.new(transaction_owner : Interro::QueryBuilder) #

[View source]
def self.new #

[View source]

Macro Detail

macro from(name, *joins) #

[View source]
macro table(name, as table_alias = nil) #

[View source]

Instance Method Detail

def &(other : self) : CompoundQuery #

[View source]
def -(other : self) : CompoundQuery #

[View source]
def |(other : self) : CompoundQuery #

[View source]
def any? : Bool #
Description copied from module Enumerable(T)

Returns true if at least one of the collection's members is truthy.

[nil, true, 99].any? # => true
[nil, false].any?    # => false
([] of Int32).any?   # => false
  • #present? does not consider truthiness of elements.
  • #any?(&) and #any(pattern) allow custom conditions.

NOTE #any? usually has the same semantics as #present?. They only differ if the element type can be falsey (i.e. T <= Nil || T <= Pointer || T <= Bool). It's typically advised to prefer #present? unless these specific truthiness semantics are required.


[View source]
def count : Int64 #

[View source]
def each #
Description copied from module Iterable(T)

Must return an Iterator over the elements in this collection.


[View source]
def each(& : T -> ) #
Description copied from module Enumerable(T)

Must yield this collection's elements to the block.


[View source]
def first(count : Int) #
Description copied from module Enumerable(T)

Returns an Array with the first count elements in the collection.

If count is bigger than the number of elements in the collection, returns as many as possible. This include the case of calling it over an empty collection, in which case it returns an empty array.


[View source]
def first #
Description copied from module Enumerable(T)

Returns the first element in the collection. Raises Enumerable::EmptyError if the collection is empty.

([1, 2, 3]).first   # => 1
([] of Int32).first # raises Enumerable::EmptyError

[View source]
def first? #
Description copied from module Enumerable(T)

Returns the first element in the collection. When the collection is empty, returns nil.

([1, 2, 3]).first?   # => 1
([] of Int32).first? # => nil

[View source]
def initialize #

[View source]
def merge(other : QueryBuilder) : self #

[View source]
def to_json(json : JSON::Builder) : Nil #

[View source]
def to_sql : String #

[View source]