All Projects → ulule → Loukoum

ulule / Loukoum

Licence: mit
A simple SQL Query Builder

Programming Languages

go
31211 projects - #10 most used programming language
golang
3204 projects

Projects that are alternatives of or similar to Loukoum

Sqliterally
Lightweight SQL query builder
Stars: ✭ 231 (-24.26%)
Mutual labels:  sql, query, query-builder, postgresql, postgres
Sqorn
A Javascript library for building SQL queries
Stars: ✭ 1,871 (+513.44%)
Mutual labels:  sql, query-builder, postgresql, postgres
Ship Hold
data access framework for Postgresql on nodejs
Stars: ✭ 110 (-63.93%)
Mutual labels:  builder, query-builder, postgresql, postgres
Fluentpdo
A PHP SQL query builder using PDO
Stars: ✭ 783 (+156.72%)
Mutual labels:  builder, sql, query, postgresql
Squid
🦑 Provides SQL tagged template strings and schema definition functions.
Stars: ✭ 57 (-81.31%)
Mutual labels:  sql, query-builder, postgresql, postgres
Timescaledb
An open-source time-series SQL database optimized for fast ingest and complex queries. Packaged as a PostgreSQL extension.
Stars: ✭ 12,211 (+3903.61%)
Mutual labels:  sql, postgresql, postgres
Sqlcell
SQLCell is a magic function for the Jupyter Notebook that executes raw, parallel, parameterized SQL queries with the ability to accept Python values as parameters and assign output data to Python variables while concurrently running Python code. And *much* more.
Stars: ✭ 145 (-52.46%)
Mutual labels:  sql, postgresql, postgres
Querybuilder
SQL query builder, written in c#, helps you build complex queries easily, supports SqlServer, MySql, PostgreSql, Oracle, Sqlite and Firebird
Stars: ✭ 2,111 (+592.13%)
Mutual labels:  sql, query-builder, postgresql
Octosql
OctoSQL is a query tool that allows you to join, analyse and transform data from multiple databases and file formats using SQL.
Stars: ✭ 2,579 (+745.57%)
Mutual labels:  sql, query, postgresql
Electrocrud
Database CRUD Application Built on Electron | MySQL, Postgres, SQLite
Stars: ✭ 1,267 (+315.41%)
Mutual labels:  sql, postgresql, postgres
Npgsql
Npgsql is the .NET data provider for PostgreSQL.
Stars: ✭ 2,415 (+691.8%)
Mutual labels:  sql, postgresql, postgres
Postgui
A React web application to query and share any PostgreSQL database.
Stars: ✭ 260 (-14.75%)
Mutual labels:  query-builder, postgresql, postgres
Postgres Checkup
Postgres Health Check and SQL Performance Analysis. 👉 THIS IS A MIRROR OF https://gitlab.com/postgres-ai/postgres-checkup
Stars: ✭ 110 (-63.93%)
Mutual labels:  sql, postgresql, postgres
Postguard
🐛 Statically validate Postgres SQL queries in JS / TS code and derive schemas.
Stars: ✭ 104 (-65.9%)
Mutual labels:  sql, query-builder, postgresql
Go Structured Query
Type safe SQL query builder and struct mapper for Go
Stars: ✭ 101 (-66.89%)
Mutual labels:  sql, query-builder, postgres
Sql Lint
An SQL linter
Stars: ✭ 243 (-20.33%)
Mutual labels:  sql, postgresql, postgres
React Querybuilder
A QueryBuilder component for React
Stars: ✭ 315 (+3.28%)
Mutual labels:  builder, query, query-builder
Sql
MySQL & PostgreSQL pipe
Stars: ✭ 81 (-73.44%)
Mutual labels:  sql, postgresql, postgres
Evolutility Server Node
Model-driven REST or GraphQL backend for CRUD and more, written in Javascript, using Node.js, Express, and PostgreSQL.
Stars: ✭ 84 (-72.46%)
Mutual labels:  sql, query-builder, postgres
Massive Js
A data mapper for Node.js and PostgreSQL.
Stars: ✭ 2,521 (+726.56%)
Mutual labels:  sql, postgresql, postgres

Loukoum

CircleCI Documentation License

A simple SQL Query Builder.

Loukoum

Introduction

Loukoum is a simple SQL Query Builder, only PostgreSQL is supported at the moment.

If you have to generate complex queries, which rely on various contexts, loukoum is the right tool for you.

Afraid to slip a tiny SQL injection manipulating fmt to append conditions? Fear no more, loukoum is here to protect you against yourself.

Just a few examples when and where loukoum can become handy:

  • Remove user anonymity if user is an admin
  • Display news draft for an author
  • Add filters in query based on request parameters
  • Add a ON CONFLICT clause for resource's owner
  • And so on...

Installation

Using Go Modules

go get github.com/ulule/loukoum/[email protected]

Usage

Loukoum helps you generate SQL queries from composable parts.

However, keep in mind it's not an ORM or a Mapper so you have to use a SQL connector (database/sql, sqlx, makroud, etc.) to execute queries.

INSERT

Insert a new Comment and retrieve its id.

import lk "github.com/ulule/loukoum/v3"

// Comment model
type Comment struct {
	ID        int64
	Email     string      `db:"email"`
	Status    string      `db:"status"`
	Message   string      `db:"message"`
	UserID    int64       `db:"user_id"`
	CreatedAt pq.NullTime `db:"created_at"`
	DeletedAt pq.NullTime `db:"deleted_at"`
}

// CreateComment creates a comment.
func CreateComment(db *sqlx.DB, comment Comment) (Comment, error) {
	builder := lk.Insert("comments").
		Set(
			lk.Pair("email", comment.Email),
			lk.Pair("status", "waiting"),
			lk.Pair("message", comment.Message),
			lk.Pair("created_at", lk.Raw("NOW()")),
		).
		Returning("id")

	// query: INSERT INTO comments (created_at, email, message, status, user_id)
	//        VALUES (NOW(), :arg_1, :arg_2, :arg_3, :arg_4) RETURNING id
	//  args: map[string]interface{}{
	//            "arg_1": string(comment.Email),
	//            "arg_2": string(comment.Message),
	//            "arg_3": string("waiting"),
	//            "arg_4": string(comment.UserID),
	//        }
	query, args := builder.NamedQuery()

	stmt, err := db.PrepareNamed(query)
	if err != nil {
		return comment, err
	}
	defer stmt.Close()

	err = stmt.Get(&comment, args)
	if err != nil {
		return comment, err
	}

	return comment, nil
}

INSERT on conflict (UPSERT)

import lk "github.com/ulule/loukoum/v3"

// UpsertComment inserts or updates a comment based on the email attribute.
func UpsertComment(db *sqlx.DB, comment Comment) (Comment, error) {
	builder := lk.Insert("comments").
		Set(
			lk.Pair("email", comment.Email),
			lk.Pair("status", "waiting"),
			lk.Pair("message", comment.Message),
			lk.Pair("user_id", comment.UserID),
			lk.Pair("created_at", lk.Raw("NOW()")),
		).
		OnConflict("email", lk.DoUpdate(
			lk.Pair("message", comment.Message),
			lk.Pair("user_id", comment.UserID),
			lk.Pair("status", "waiting"),
			lk.Pair("created_at", lk.Raw("NOW()")),
			lk.Pair("deleted_at", nil),
		)).
		Returning("id, created_at")

	// query: INSERT INTO comments (created_at, email, message, status, user_id)
	//        VALUES (NOW(), :arg_1, :arg_2, :arg_3, :arg_4)
	//        ON CONFLICT (email) DO UPDATE SET created_at = NOW(), deleted_at = NULL, message = :arg_5,
	//        status = :arg_6, user_id = :arg_7 RETURNING id, created_at
	//  args: map[string]interface{}{
	//            "arg_1": string(comment.Email),
	//            "arg_2": string(comment.Message),
	//            "arg_3": string("waiting"),
	//            "arg_4": string(comment.UserID),
	//            "arg_5": string(comment.Message),
	//            "arg_6": string("waiting"),
	//            "arg_7": string(comment.UserID),
	//        }
	query, args := builder.NamedQuery()

	stmt, err := db.PrepareNamed(query)
	if err != nil {
		return comment, err
	}
	defer stmt.Close()

	err = stmt.Get(&comment, args)
	if err != nil {
		return comment, err
	}

	return comment, nil
}

UPDATE

Publish a News by updating its status and publication date.

// News model
type News struct {
	ID          int64
	Status      string      `db:"status"`
	PublishedAt pq.NullTime `db:"published_at"`
	DeletedAt   pq.NullTime `db:"deleted_at"`
}

// PublishNews publishes a news.
func PublishNews(db *sqlx.DB, news News) (News, error) {
	builder := lk.Update("news").
		Set(
			lk.Pair("published_at", lk.Raw("NOW()")),
			lk.Pair("status", "published"),
		).
		Where(lk.Condition("id").Equal(news.ID)).
		And(lk.Condition("deleted_at").IsNull(true)).
		Returning("published_at")

	// query: UPDATE news SET published_at = NOW(), status = :arg_1 WHERE ((id = :arg_2) AND (deleted_at IS NULL))
	//        RETURNING published_at
	//  args: map[string]interface{}{
	//            "arg_1": string("published"),
	//            "arg_2": int64(news.ID),
	//        }
	query, args := builder.NamedQuery()

	stmt, err := db.PrepareNamed(query)
	if err != nil {
		return news, err
	}
	defer stmt.Close()

	err = stmt.Get(&news, args)
	if err != nil {
		return news, err
	}

	return news, nil
}

SELECT

Basic SELECT with an unique condition

Retrieve non-deleted users.

import lk "github.com/ulule/loukoum/v3"

// User model
type User struct {
	ID int64

	FirstName string `db:"first_name"`
	LastName  string `db:"last_name"`
	Email     string
	IsStaff   bool        `db:"is_staff"`
	DeletedAt pq.NullTime `db:"deleted_at"`
}

// FindUsers retrieves non-deleted users
func FindUsers(db *sqlx.DB) ([]User, error) {
	builder := lk.Select("id", "first_name", "last_name", "email").
		From("users").
		Where(lk.Condition("deleted_at").IsNull(true))

	// query: SELECT id, first_name, last_name, email FROM users WHERE (deleted_at IS NULL)
	//  args: map[string]interface{}{
	//
	//        }
	query, args := builder.NamedQuery()

	stmt, err := db.PrepareNamed(query)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	users := []User{}

	err = stmt.Select(&users, args)
	if err != nil {
		return nil, err
	}

	return users, nil
}

SELECT IN with subquery

Retrieve comments only sent by staff users, the staff users query will be a subquery as we don't want to use any JOIN operations.

// FindStaffComments retrieves comments by staff users.
func FindStaffComments(db *sqlx.DB, comment Comment) ([]Comment, error) {
	builder := lk.Select("id", "email", "status", "user_id", "message", "created_at").
		From("comments").
		Where(lk.Condition("deleted_at").IsNull(true)).
		Where(
			lk.Condition("user_id").In(
				lk.Select("id").
					From("users").
					Where(lk.Condition("is_staff").Equal(true)),
			),
		)

	// query: SELECT id, email, status, user_id, message, created_at
	//        FROM comments WHERE ((deleted_at IS NULL) AND
	//        (user_id IN (SELECT id FROM users WHERE (is_staff = :arg_1))))
	//  args: map[string]interface{}{
	//            "arg_1": bool(true),
	//        }
	query, args := builder.NamedQuery()

	stmt, err := db.PrepareNamed(query)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	comments := []Comment{}

	err = stmt.Select(&comments, args)
	if err != nil {
		return nil, err
	}

	return comments, nil
}

SELECT with JOIN

Retrieve non-deleted comments sent by a user with embedded user in results.

First, we need to update the Comment struct to embed User.

// Comment model
type Comment struct {
	ID        int64
	Email     string      `db:"email"`
	Status    string      `db:"status"`
	Message   string      `db:"message"`
	UserID    int64       `db:"user_id"`
	User      *User       `db:"users"`
	CreatedAt pq.NullTime `db:"created_at"`
	DeletedAt pq.NullTime `db:"deleted_at"`
}

Let's create a FindComments method to retrieve these comments.

In this scenario we will use an INNER JOIN but loukoum also supports LEFT JOIN and RIGHT JOIN.

// FindComments retrieves comments by users.
func FindComments(db *sqlx.DB, comment Comment) ([]Comment, error) {
	builder := lk.
		Select(
			"comments.id", "comments.email", "comments.status",
			"comments.user_id", "comments.message", "comments.created_at",
		).
		From("comments").
		Join(lk.Table("users"), lk.On("comments.user_id", "users.id")).
		Where(lk.Condition("comments.deleted_at").IsNull(true))

	// query: SELECT comments.id, comments.email, comments.status, comments.user_id, comments.message,
	//        comments.created_at FROM comments INNER JOIN users ON comments.user_id = users.id
	//        WHERE (comments.deleted_at IS NULL)
	//  args: map[string]interface{}{
	//
	//        }
	query, args := builder.NamedQuery()

	stmt, err := db.PrepareNamed(query)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	comments := []Comment{}

	err = stmt.Select(&comments, args)
	if err != nil {
		return nil, err
	}

	return comments, nil
}

DELETE

Delete a user based on ID.

// DeleteUser deletes a user.
func DeleteUser(db *sqlx.DB, user User) error {
	builder := lk.Delete("users").
		Where(lk.Condition("id").Equal(user.ID))


	// query: DELETE FROM users WHERE (id = :arg_1)
	//  args: map[string]interface{}{
	//            "arg_1": int64(user.ID),
	//        }
	query, args := builder.NamedQuery()

	stmt, err := db.PrepareNamed(query)
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(args)

	return err
}

See examples directory for more information.

NOTE: For database/sql, see standard.

Migration

Migrating from v2.x.x

  • Migrate from dep to go modules by replacing the import path github.com/ulule/loukoum/... by github.com/ulule/loukoum/v3/...

Migrating from v1.x.x

Inspiration

Thanks

License

This is Free Software, released under the MIT License.

Loukoum artworks are released under the Creative Commons BY-SA License.

Contributing

Don't hesitate ;)

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].