# Query Builder

Cotton offers a simple, powerful, and database agnostic query builder. It allows you to construct SQL queries with ease.

Once you have established a connection, you can now use the query builder via table method.

const db = connect({
  type: "sqlite",
  // other options...
});

const users = await db.table("users").execute(); // SELECT * FROM `users`;

for (const user of users) {
  console.log(user); // { id: 1, email: 'a@b.com', ... }
}

The table methods takes only one argument, which is the name of the table you want to fetch. This method returns a QueryBuilder instance, which contains more methods to construct your query. You can chain all these methods you need in order to add more constraints or statements.

// SELECT * FROM `users` WHERE `id` = ?;
const users = db.table("users").where("id", 1).execute();

As you can see, the values are replaced with a placeholder in the query string and being handled by the database to prevent SQL injection.

If you just want to get the SQL query string and don't want run the it, you can end the statement with toSQL instead of execute.

const { text, values } = db.table("users").where("id", 1).toSQL();
console.log(text); // "SELECT * FROM `users` WHERE `id` = ?;"
console.log(values); // [1]

# WHERE

One of the most common thing people do in SQL query is using WHERE clause to filter the result.

Filtering records through WHERE clause is one of the most common thing people do with SQL query. In Cotton, you can easily add where clause of any kind by simply using where method.

db.table("users").where("id", 1);

The first parameter is the column name, and the second is the expected value. By default, it will use the = operator which will check whether the value is equal. You can customize the query operator by using Q helper to customize the SQL expressions.

db.table("users").where("id", Q.gt(1)); // SELECT * FROM `users` WHERE `id` > 1;
db.table("users").where("id", Q.in([1, 2, 3])); // SELECT * FROM `users` WHERE `id` IN (1, 2, 3);
db.table("users").where("id", Q.between([1, 5])); // SELECT * FROM `users` WHERE `id` BETWEEN 1 AND 5

These are the valid expressions you can access through Q utility.

Syntax SQL Equivalent Description
Q.in([1, 2, 3]) IN The value is one of the given values
Q.notIn([1, 2, 3]) NOT IN The value is not one of the given values
Q.between(5, 10) BETWEEN The value (number) is between two numbers
Q.notBetween(5, 10) NOT BETWEEN The value (number) is between two numbers
Q.like('%john%') LIKE LIKE operator
Q.notLike('%john%') NOT LIKE NOT LIKE operator
Q.ilike('%john%') ILIKE ILIKE (case-insensitive) operator
Q.notIlike('%john%') NOT ILIKE NOT ILIKE (case-insensitive) operator
Q.eq('a@b.com') = Is equal to
Q.neq('a@b.com') != Is not equal to
Q.gt(7) > Greater than
Q.gte(7) >= Greater than equal
Q.lt(7) < Lower than
Q.lte(7) <= Lower than equal
Q.null() IS NULL Is the value null
Q.notNull() IS NOT NULL Is the value not null

Currently, the valid values are boolean, string, null, number, and Date.

query
  .table("users")
  .where("email", "a@b.com")
  .where("age", Q.gte(16))
  .where("is_active", true)
  .where("birthday", Q.lt(new Date("7 July, 2020")));

Sometimes you want to exclude or some records that match given conditions. For that, you can use not.

query.table("users").not("is_active", true);

Or, if you want to find records if one of the conditions are true, use or.

query.table("users").where("name", "John").or("name", "Jane");

# SELECT

By default, query builder will select every single columns in the table with *. However, you can choose which columsnt to select in a query by calling select.

db.table("users").select("email");

To select multiple columns, you can either pass multiple strings to the parameter or chain it multiple times.

db.table("users").select("email", "age", "is_active");

// Alternatively...
db.table("users").select("email").select("age").select("is_active");

If you want to select only unique values, you can enable distinct select by using distinct method.

db.table("users").select("email").distinct();

# GROUP BY & HAVING

Adding a GROUP BY query can be done by using groupBy.

db.table("users").groupBy("category");
db.table("users").groupBy("users.category"); // explicit table name

Most of the time, GROUP BY expressions are paired with HAVING to filter the records, and here is how you can do that:

db.table("users").groupBy("category").having("is_active", false);

# ORDER

You can sort a column using order.

db.table("users").order("age");

By default this will sort in ascending order. You can change this by passing the second parameter.

db.table("users").order("age", "DESC"); // or ASC

To sort multiple column, you can chain this method as many as you want.

db.table("users").order("age", "DESC").order("created_at");

# COUNT

You can count how many records that match given conditions by using the count method.

db.table("users").count("is_active");

Count multiple columns by passing an array as the argument.

db.table("users").count(["is_active", "is_banned"]);

To specify an alias for the count result, you can pass the second parameter.

db.table("users").count("is_active", "a");

Use countDistinct to add distinct expression inside your count statement.

db.table("users").countDistinct("is_active", "a");

# OFFSET & LIMIT

Typically, pagination can be done in SQL by using limit and offset. Limit is the maximum number of record to return, and the offset is the number of records to skip. Here is an example.

db.table("users").limit(5).offset(10);

# INSERT / REPLACE

To insert a new record, use insert.

db.table("users").insert({ email: "a@b.com", age: 16 });

To insert multiple records in a single query, you can pass an array instead.

db.table("users").insert([
  { email: "a@b.com", age: 16 },
  { email: "b@c.com", age: 17 },
  { email: "c@d.com", age: 18 },
]);

Another way to insert a record is by using replace. It will look for PRIMARY and UNIQUE constraints. If something matched, it gets removed from the table and creates a new row with the given values.

db.table("users").replace({ email: "a@b.com", age: 16 });

db.table("users").replace([
  { email: "a@b.com", age: 16 },
  // ...
]);

# UPDATE

To perform update, you need to chain update method and pass the values you want to update. The value parameter is a key-value pair which represents the column name and it's value. This method can be chained with other constraints such as where, not, or, limit, etc.

db.table("users").where("id", 1).update({ email: "a@b.com" });

# DELETE

The only thing you need to do to perform DELETE query is by adding delete method to the query builder.

db.table("users").where("id", 1).delete();

# RETURNING

Returning is a statement that typically used in INSERT or REPLACE query. Note that this feature only works in PostgreSQL. However, you can still build this query in MySQL or SQLite connection.

db.table("users").insert({ email: "a@b.com" }).returning("id", "email");