Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build dynamic (conditional) WHERE SQL query in Golang

Tags:

printf

orm

go

I'm using golang, go_reform, PostgreSQL. What i want to do is a REST search utility, and all went fine until I faced with conditional search query. "Conditional" here means I have 10 columns in a table to search in, and there may be ton of combinations so I can't handle them all separately. What i need is a query builder, but I have no understanding how can I implement this in Go. For now I have an idea like this, but it seems not really efficient

type Query struct {
    Id               *int64
    FirstName        *string
    MiddleName       *string
    LastName         *string
    AreaId           *int64
    Birthday         *time.Time
}

func (table *Query) Find() (*User) {
    if table.Id != nil {
        idstr := fmt.Sprintf("WHERE Id = %d AND ", table.Id)
    }
    else idstr := "WHERE "
    }
    if table.FirstName != "" {
        firststr := fmt.Sprintf("FirstName = %s AND", table.FirstName)
    }
    else firststr := ""
}//and so on

That feels really awkward so I'm wondering is there any better way to determine the fields that came to the Find() and build a SQL query based on this. (Actually it's coming in JSON and binding to Query struct, so maybe there is a way without struct). There also may be SQL workarounds, but I think it would be more efficient to build query without all possible columns.

EDIT: By the way, making my Google search query more accurate, i found a bunch of things related to my problem, probably i will try to use it now. For those who interested too: old go playground example

Making dynamic SQL queries to a MySQL DB

gorp package (snippets thing sounds very promising)

like image 861
Pavel Nasevich Avatar asked Sep 14 '18 08:09

Pavel Nasevich


2 Answers

So, I found the solution. Big thanks to Cerise Limón, whose code fits perfectly for me.

The solution I ended up with

Controller

func Find(c echo.Context) (err error) {
model := &models.Query{}
if err = c.Bind(model); err != nil {
    return c.JSON(http.StatusInternalServerError, u.Message(false, "Bad request"))
}
resp := model.Find()
return c.JSON(http.StatusOK, resp)

Model

type Query map[string]interface{}

func (model Query) Find() (Query) {
    var values []interface{}
    var where []string
    for k, v := range model {
        values = append(values, v)
        //MySQL Way: where = append(where, fmt.Sprintf("%s = ?", k))
        where = append(where, fmt.Sprintf(`"%s" = %s`,k, "$" + strconv.Itoa(len(values))))
    }
    string := ("SELECT name FROM users WHERE " + strings.Join(where, " AND "))
    //for testing purposes i didn't ran actual query, just print it in the console and returned JSON back
    fmt.Println(string)
    return model

}

Update: for PostgreSQL users (thanks to @mkopriva and his playground example), I'm able to have this placeholder thing working right on PostgreSQL

like image 81
Pavel Nasevich Avatar answered Sep 19 '22 22:09

Pavel Nasevich


In orm GORM,we do it like

if con1 {
    db.Where("con1 =?", con1Flag)
}

If the orm you write yoursefl, I suggest change into gorm.Or you can refer to the orm you're using ,whether it has the same ussage like code above. If you're coding yourself, do it whatever you like. If you'r working in group, I guess using a mature orm is much better

like image 35
fwhez Avatar answered Sep 19 '22 22:09

fwhez