-
Notifications
You must be signed in to change notification settings - Fork 27
Query interface
Code examples throughout this guide will refer to one or more of the following models:
package models
case class Client(name: String, age: Int) extends ActiveRecord {
lazy val orders = hasMany[Order]
lazy val roles = hasAndBelongToMany[Role]
}
object Client extends ActiveRecordCompanion[Client]
case class Order(price: Int) extends ActiveRecord {
val clientId: Option[Long] = None
val categoryId: Option[Long] = None
lazy val client = belongsTo[Client]
lazy val category = belongsTo[Category]
}
object Order extends ActiveRecordCompanion[Order]
case class Role(name: String) extends ActiveRecord {
lazy val clients = hasAndBelongToMany[Role]
}
object Role extends ActiveRecordCompanion[Role]
case class Category(name: String) extends ActiveRecord
object Category extends ActiveRecordCompanion[Category] {
lazy val orders = hasMany[Order]
}
object Tables extends ActiveRecordTables {
val clients = table[Client]
val orders = table[Order]
val roles = table[Role]
}
Using Model.find(primaryKey)
, you can retrieve the object Option[Model]
corresponding to the specified primary key that matches any supplied options. For example:
val client = Client.find(10)
// => Some(Client) or None
Using Model.findBy((key, value)*)
finds the first record by multiple fieldnames and values. For example:
val john = Client.findBy("name", "john")
// => Some(Client("john")) or None
val john25 = Client.findBy("name" -> "john", "age" -> 25)
// => Some(Client("john", 25)) or None
Using Model.findByOrCreate(model, fields*)
returns either the record that already exists or the new record. For example:
val client = Client.findByOrCreate(Client("john", 25), "name", "age")
// => found Client("john", 25) or created Client("john", 25)
Defined implicit conversion from companion model Model
to scala.collection.Iterable
.
For example:
val client1 = Client.head
// => First client or java.util.NoSuchElementException
val client2 = Client.headOption
// => First Option[Client] or None
val client3 = Client.lastOption
// => Last Option[Client] or None
val (adults, children) = Client.partition(_.age >= 20)
// => parts of clients
To retrieve records from the database, you can use the where
method with the retrieval conditions. For example:
Clients.where(_.name === "john").toList
Clients.where(_.name === "john").headOption
Clients.where(c => c.name === "john" and c.age le 25).toList
Clients.where(c => c.name === "john" and c.age.~ > 25).toList
Clients.where(_.name === "john").where(_.age.~ > 25).toList
Clients.where(_.name like "john%").toList
Clients.where(_.name regex ".*oh.*").toList
Boolean operators:
not, isNull, isNotNull, between, ===, <, lt, >, gt, <=, lte, <=, gte, <>, exists, notExists, in, notIn
Arithmetic operators:
plus, +, minus, -, times, *, div, /
String operators:
|| (concatenation), lower, upper, like, regex
Note : To avoid the ambiguity in the numeric fields, you must call
.~
method before using comparison operators like<
or>
.
Client.not(_.name == "foobar").toList
Client.where(_.age.~ > 24).not(_.name == "foobar").toList
Using inhibitWhen
from Squeryl.
// returns all clients if name == None
def clients(name: Option[String]): List[Client] = {
Clients.where(_.name === name.inhibitWhen(name == None)).toList
}
// using .? operator
def clients(name: Option[String]): List[Client] = {
Clients.where(_.name === name.?).toList
}
Using Model.findByAll((key, value)*)
finds records by multiple fieldnames and values. For example:
val clients = Clients.findAllBy("name" -> "john", "age" -> 25)
// => found clients
To retrieve records from the database in a specific order, you can use the orderBy
method:
// simple order (ORDER BY user_name):
Client.orderBy(_.name)
// set order (use for 'asc' or 'desc'):
Client.orderBy(_.name asc)
// after other query (it's non-nested query!):
Client.where(_.name === "john").orderBy(_.name desc)
// ordering by multiple fields:
Client.orderBy(_.name asc, _.age desc)
Query will return the List[S]
from any legal Scala expression, you can use the select
method:
Client.select(_.name).toList
// => List[String]
Client.select(u => (u.name, u.age)).toList
// => List[(String, Int)]
// Arbitrary select expression on the database side
Client.select(c => &("name:" || c.name || ", age:" || c.age)).toList
// => List[String]
If you would like to only grab a single record per unique value in a certain field, you can use distinct
:
Client.select(_.name).distinct.toList
This would generate SQL like:
SELECT DISTINCT name FROM clients
limit
returns only specified count.
Client.limit(10)
page(offset, pageLength)
returns paginated records.
Client.page(2, 5)
exists(condition)
returns true/false.
Client.exists(_.name === "john")
Client.exists(_.name like "jo%")
Client.exists(c => c.name like "jo%" and c.age.~ > 25)
Client.where(_.age.~ > 20).exists(_.name like "john")
count
returns records counts.
Client.where(_.name regex ".*oh.*").count
// => 20 or 0
Client.max(_.age)
Client.maximum(_.age)
// => Some(20) or None
Client.min(_.age)
Client.minimum(_.age)
// => Some(20) or None
Client.avg(_.age)
Client.average(_.age)
// => Some(20) or None
Client.sum(_.age)
// => Some(20) or None
Client.compute(client => max(client.age))
// => Some(20) or None
Note : Available version
0.3.1
or later.
Allows to specify a GROUP
clause.
SELECT "clients".* FROM "clients" GROUP BY name
count
returns records counts by groups.
Client.group(_.age).count
// => Map(10 -> 1, 20 -> 2, 25 -> 1, 30 -> 5)
Client.group(_.name).max(_.age)
Client.group(_.name).maximum(_.age)
// => Map("tom" -> Some(20), "john" -> Some(30))
Client.group(_.name).min(_.age)
Client.group(_.name).minimum(_.age)
// => Map("tom" -> Some(10), "john" -> Some(25))
Client.group(_.name).avg(_.age)
Client.group(_.name).average(_.age)
// => Map("tom" -> Some(22.4), "john" -> Some(28.5))
Client.group(_.name).sum(_.age)
// => Map("tom" -> Some(80), "john" -> Some(120))
Client.group(_.name).compute(client => max(client.age))
// => Map("tom" -> Some(20), "john" -> Some(30))
Allows to specify a HAVING
clause.
Client.group(_.name).having(_.name === "tom").max(_.age)
// => Map("tom" -> Some(20))
Client.group(_.name).having(client => avg(client.age) > 20).sum(_.age)
// => Map("john" -> Some(120))
joins[OtherModel]((SelfModel, OtherModel) => joinOnCondition))
returns joined query:
Client.joins[Order](
// join on
(client, order) => client.id === order.clientId
).where(
(client, order) => client.age.~ < 20 and order.price.~ > 10000
).select(
(client, order) => (client.name, client.age, order.price)
).toList
This produces:
Select
clients.name,
clients.age,
orders.price
From
clients inner join orders on (clients.id = orders.client_id)
Where
((clients.age < 20) and (groups4.price > 10000))
joins[OtherModel1, OtherModel2]((SelfModel, OtherModel1, OtherModel2) => (joinOnCondition1, joinOnCondition2))
returns multiple joined query:
Client.joins[Order, Category](
// join on
(client, order, category) => (client.id === order.clientId, order.categoryId === category.id)
).where(
(client, order, category) => client.age.~ < 20 and order.price.~ > 10000 and category.name === "computer"
).select(
(client, order, category) => (client.name, client.age, order.price, category.name)
).toList
This produces:
Select
clients.name,
clients.age,
orders.price,
categories.name
From users
inner join orders on (users.group_id = orders.id)
inner join categories on (orders.category_id = categories.id)
Where
((clients.age < 20) and (orders.price > 10000) and (categories.name = "computer"))
You can load association while loading object using includes(association)
. (solution for the n+1 query problem)
For example:
Order.includes(_.client).limit(10).foreach { order =>
println(order.client.name)
}
This produces:
Select
orders.price,
orders.id
From
orders
limit 10 offset 0;
Select
clients.name,
clients.age,
clients.id
From
clients inner join orders on (clients.id = orders.client_id)
Where
(orders.id in (1,2,3,4,5,6,7,8,9,10))
relation#reload
reloads the attributes of this object from the database:
val orders = Order.where(_.price > 2000)
println(orders.toList) // execute SQL query, and cached query. (1)
Order(3000).save
println(orders.toList) // non-execute SQL query. equal the list of (1)
println(orders.reload) // execute SQL query, and cached query.