querybuilder is a little scala library that uses scalikejdbc to create
SQL queries that select rows from a database.
The query can be defined as case class or as json.
For the examples we assume that the following table was created in the database
create table if not exists employees (
id serial not null primary key,
name varchar(64),
dateofbirth date,
city varchar(64),
salary intand we model the database with the following scala case class.
case class Employee(
id: Int,
name: String,
dateOfBirth: java.sql.Date,
city: String,
salary: Int
)
object Employee extends SQLSyntaxSupport[Employee] {
override val schemaName = None
override val tableName = "employees"
override val useSnakeCaseColumnName = false
}
val alias = "t"
implicit val t = Employee.syntax(alias)Now, we can use
val json =
"""
|{
| "columns": ["city", {"func": "avg", "column": "salary"}],
| "distinct": false,
| "filter": { "type": ">", "lhs": "id", "rhs": 12 }
|}
""".stripMargin
val query = read[Query](json)
val b = new ScalikeQueryBuilder[Employee].build(query)
println(b.sql)The generated SQL statement is
select t.city, avg(t.salary) from employees t where t.id > ? group by t.cityThe query can be run with
val result: Seq[Map[String, Any]] = b.query()The input JSON must have the following format:
{
"columns": [...],
"distinct": true|false,
"filter": ... (optional),
"limit": Int (optional)
}
The required content of the fields is specified in the following sections.
A column can be a simple string specifying a column that should be selected from the table. To obtain a column under a different name, the following JSON object can be used
{
"column": "nameInTable",
"as": "newName"
}It is also possible to use an aggregation function on a column. The JSON object has to have the following format
{
"func": "sum" | "avg" | "ceil" | "floor" | "abs" | "min" | "max" | "count",
"column": "nameInTable",
"as": "newName"
}
The functions sum and count can also contain the optional distinct
flag, i.e.
{
"func": "sum" | "count",
"column": "nameInTable",
"as": "newName",
"distinct": true | false
}
A filter is defined as tree of filter functions.
The basic filter type is a binary filter, e.g. the = comparison.
They can be specified with the following JSON object
{
"type": "=" | ">" | "<" | ">=" | "<=" | "<>",
"lhs": "columnName",
"rhs": value | { "column": "columnName" }
}
The filters between and not between are also supported via
{
"type": "between" | "not between",
"lower": value,
"upper": value
}
The filters is null and is not null are supported via
{
"type": "is null" | "is not null",
"column": "columnName"
}
The filter like is supported via
{
"type": "like",
"column": "columnName",
"pattern": "pattern"
}More complex filters can be created by using And and Or.
{
"type": "and" | "or",
"filters": [list of filters]
}
For example the filter
x = ? AND y IS NOT NULLcan be specified as
{
"type":"and",
"filters": [
{
"type": "=",
"lhs": "x",
"rhs": 0
},
{
"type": "is not null",
"column": "y"
}
]
}Also and and or can be chained, i.e.
(id = ? or id = ?) and (name = ? or name = ?)is equivalent to
{
"type": "and",
"filters": [
{
"type": "or",
"filters": [
{ "type": "=", "lhs": "id", "rhs": 1 },
{ "type": "=", "lhs": "id", "rhs": 2 }
]
}, {
"type": "or",
"filters": [
{ "type": "=", "lhs": "name", "rhs": "peter" },
{ "type": "=", "lhs": "name", "rhs": "steve" }
]
},
]
}The not operator is available via
{
"type": "not",
"filter": some filter
}