I want to create a query like this CASE WHEN but i do not see any option or anything like that in docs
SELECT id,
CASE WHEN title ILIKE '%bag%' THEN 0 ELSE 1 END AS title_match,
title
FROM items as i WHERE title ILIKE '%bag%'
ORDER BY title_match
how do i achieve the case when query
Expected answer
I want to add something like this to my query CASE WHEN title ILIKE '%bag%' THEN 0 ELSE 1 END AS title_match
Comment From: jinzhu
Select accepts SQL expression
Comment From: Reidsy
Commenting on this because it is high up on google when searching for CASE WHEN with Gorm.
This is by no means production quality code, but may give others a starting point who want an alternative other than raw sql.
import (
"fmt"
"gorm.io/gorm/clause"
)
type OrderByCase struct {
Column clause.Column
Values map[string]int
Desc bool
}
// Name where clause name
func (orderBy OrderByCase) Name() string {
return "ORDER BY"
}
// Build build where clause
func (orderByCase OrderByCase) Build(builder clause.Builder) {
builder.WriteString("CASE ")
builder.WriteQuoted(orderByCase.Column)
for field, weight := range orderByCase.Values {
builder.WriteString(fmt.Sprintf(" WHEN '%s' THEN %d", field, weight)) // potential sql injection risk
}
builder.WriteString(" END")
if orderByCase.Desc {
builder.WriteString(" DESC")
}
}
// MergeClause merge order by clauses
func (orderByCase OrderByCase) MergeClause(clause *clause.Clause) {
clause.Expression = orderByCase
}
Usage is like the following:
u := User{}
db.ToSQL(func(tx *gorm.DB) *gorm.DB {
return tx.Clauses(
OrderByCase{
Column: clause.Column{Name: "title"},
Values: map[string]int{
"STAFF": 10,
"CUSTOMER": 20,
},
Desc: true,
},
).Limit(1).Find(&u)
To produce the SQL
SELECT * FROM "users"
ORDER BY CASE "title"
WHEN 'STAFF' THEN 10
WHEN 'CUSTOMER' THEN 20
END DESC
LIMIT 1
Comment From: dgunay
Fair warning that GORM tends to stomp on @Reidsy 's solution. Example: if you use .First then GORM overwrites the existing ORDER BY clause with its own, since it is only able to merge with other clause.OrderBys: https://github.com/go-gorm/gorm/blob/8fb9a317756bc07dcaaa82d43613ddcf9295c1ad/clause/order_by.go#L39-L53
I was able to get multiple ORDER BY clauses to play nice together by doing this:
tx := db.
Where("etc = ?", 1).
Clauses(
clause.OrderBy{
Expression: clause.CommaExpression{
Exprs: []clause.Expression{
OrderByCase{ /* etc */ },
clause.Expr{SQL: "created_at DESC"},
},
},
},
).
Find(&entity)
Comment From: rblaszcz
I would like to expand @jinzhu's answer because other answers focus into putting CASE inside ORDER BY clause instead of SELECT which is not what the OP was asking for.
The simplest and gorm compatible way to achieve this is to use CASE inside SELECT statement using gorm's Select method which can take SQL code. By creating a new column using CASE inside SELECT and naming this column you can then sort by this new column's name in ORDER BY clause.
example code:
res := db.
Select("CASE status WHEN 'BLOCKED' THEN 1 WHEN 'COMPLETED' THEN 10 ELSE 5 END AS status_order").
Order("status_order").
Find(&entities)