Skip to content

Commit

Permalink
clean surface API, finishers
Browse files Browse the repository at this point in the history
  • Loading branch information
amirrezaask committed Mar 17, 2022
1 parent dfa0663 commit a405c1c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 48 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ GoLobby ORM is a lightweight yet powerful, fast, customizable, type-safe object-
- [BelongsToMany](#belongstomany)
- [Saving with relation](#saving-with-relation)
+ [Query Builder](#query-builder)
- [Finishers](#finishers)
* [All](#all)
* [Get](#get)
* [Update](#update)
* [Delete](#delete)
- [Select](#select)
* [Column names](#column-names-1)
* [Table](#table)
Expand Down Expand Up @@ -326,6 +331,30 @@ orm.Add(post, comments...) // inserts all comments passed in and also sets all p
GoLobby ORM contains a powerful query builder to help you build complex queries with ease. QueryBuilder is accessible from `orm.Query[Entity]` method
which will create a new query builder for you with given type parameter.
Query builder can build `SELECT`,`UPDATE`,`DELETE` queries for you.

#### Finishers
Finishers are methods on QueryBuilder that will some how touch database, so use them with caution.
##### All
All will generate a `SELECT` query from QueryBuilder, execute it on database and return results in a slice of OUTPUT. It's useful for queries that have multiple results.
```go
posts, err := orm.Query[Post]().All()
```
##### Get
Get will generate a `SELECT` query from QueryBuilder, execute it on database and return results in an instance of type parameter `OUTPUT`. It's useful for when you know your query has single result.
have multiple results.
```go
post, err := orm.Query[Post]().All()
```
##### Update
Update will generate an `UPDATE` query from QueryBuilder and executes it, returns rows affected by query and any possible error.
```go
rowsAffected, err := orm.Query[Post]().WherePK(1).Set("body", "body jadid").Update()
```
##### Delete
Delete will generate a `DELETE` query from QueryBuilder and executes it, returns rows affected by query and any possible error.
```go
rowsAffected, err := orm.Query[Post]().WherePK(1).Delete()
```
#### Select
Let's start with `Select` queries.
Each `Select` query consists of following:
Expand Down
16 changes: 3 additions & 13 deletions orm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,12 +414,7 @@ func TestQuery(t *testing.T) {
setup(t)
assert.NoError(t, orm.Save(&Post{BodyText: "body 1"}))

res, err := orm.Query[Post]().Where("id", 1).Update(orm.KV{
"body": "body jadid",
})
assert.NoError(t, err)

affected, err := res.RowsAffected()
affected, err := orm.Query[Post]().Where("id", 1).Set("body", "body jadid").Update()
assert.NoError(t, err)
assert.EqualValues(t, 1, affected)

Expand All @@ -432,8 +427,9 @@ func TestQuery(t *testing.T) {
setup(t)
assert.NoError(t, orm.Save(&Post{BodyText: "body 1"}))

_, err := orm.Query[Post]().WherePK(1).Delete()
affected, err := orm.Query[Post]().WherePK(1).Delete()
assert.NoError(t, err)
assert.EqualValues(t, 1, affected)
count, err := orm.Query[Post]().WherePK(1).Count().Get()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
Expand All @@ -456,10 +452,4 @@ func TestQuery(t *testing.T) {

assert.EqualValues(t, "body 2", post.BodyText)
})

t.Run("use .Execute when query type is select", func(t *testing.T) {
setup(t)
_, err := orm.Query[Post]().SetSelect().Execute()
assert.Error(t, err)
})
}
65 changes: 30 additions & 35 deletions query.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ type QueryBuilder[OUTPUT any] struct {

// Finisher APIs

// Execute executes QueryBuilder query, remember to use this when you have an Update
// execute is a finisher executes QueryBuilder query, remember to use this when you have an Update
// or Delete Query.
func (q *QueryBuilder[OUTPUT]) Execute() (sql.Result, error) {
func (q *QueryBuilder[OUTPUT]) execute() (sql.Result, error) {
if q.err != nil {
return nil, q.err
}
Expand All @@ -61,8 +61,7 @@ func (q *QueryBuilder[OUTPUT]) Execute() (sql.Result, error) {
return q.schema.getConnection().exec(query, args...)
}

// Get runs query generated by query builder,
// it's used for when you know your query is going to return single row.
// Get limit results to 1, runs query generated by query builder, scans result into OUTPUT.
func (q *QueryBuilder[OUTPUT]) Get() (OUTPUT, error) {
if q.err != nil {
return *new(OUTPUT), q.err
Expand All @@ -83,7 +82,7 @@ func (q *QueryBuilder[OUTPUT]) Get() (OUTPUT, error) {
return output, nil
}

// All create the Select query based on QueryBuilder and scan results into
// All is a finisher, create the Select query based on QueryBuilder and scan results into
// slice of type parameter E.
func (q *QueryBuilder[OUTPUT]) All() ([]OUTPUT, error) {
if q.err != nil {
Expand All @@ -106,6 +105,32 @@ func (q *QueryBuilder[OUTPUT]) All() ([]OUTPUT, error) {
return output, nil
}

// Delete is a finisher, creates a delete query from query builder and executes it.
func (q *QueryBuilder[OUTPUT]) Delete() (rowsAffected int64, err error) {
if q.err != nil {
return 0, q.err
}
q.SetDelete()
res, err := q.execute()
if err != nil {
return 0, q.err
}
return res.RowsAffected()
}

// Update is a finisher, creates an Update query from QueryBuilder and executes in into database, returns
func (q *QueryBuilder[OUTPUT]) Update() (rowsAffected int64, err error) {
if q.err != nil {
return 0, q.err
}
q.SetUpdate()
res, err := q.execute()
if err != nil {
return 0, err
}
return res.RowsAffected()
}

func copyQueryBuilder[T1 any, T2 any](q *QueryBuilder[T1], q2 *QueryBuilder[T2]) {
q2.db = q.db
q2.err = q.err
Expand Down Expand Up @@ -156,36 +181,6 @@ func (q *QueryBuilder[OUTPUT]) WherePK(value interface{}) *QueryBuilder[OUTPUT]
return q.Where(q.schema.pkName(), value)
}

// Delete sets QueryBuilder type to be delete and then Executes it.
func (q *QueryBuilder[OUTPUT]) Delete() (sql.Result, error) {
if q.err != nil {
return nil, q.err
}
q.SetDelete()
return q.Execute()
}

func asTuples(toUpdate map[string]interface{}) [][2]interface{} {
var tuples [][2]interface{}
for k, v := range toUpdate {
tuples = append(tuples, [2]interface{}{k, v})
}
return tuples
}

type KV = map[string]interface{}

// Update creates an Update query from QueryBuilder and executes in into database, also adds all given key values in
// argument to list of key values of update query.
func (q *QueryBuilder[OUTPUT]) Update(toUpdate map[string]interface{}) (sql.Result, error) {
if q.err != nil {
return nil, q.err
}
q.SetUpdate()
q.sets = append(q.sets, asTuples(toUpdate)...)
return q.Execute()
}

func (d *QueryBuilder[OUTPUT]) toSqlDelete() (string, []interface{}, error) {
base := fmt.Sprintf("DELETE FROM %s", d.table)
var args []interface{}
Expand Down

0 comments on commit a405c1c

Please sign in to comment.