diff --git a/README.md b/README.md index be54541..b0b4404 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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: diff --git a/orm_test.go b/orm_test.go index d6ec180..26df61e 100644 --- a/orm_test.go +++ b/orm_test.go @@ -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) @@ -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) @@ -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) - }) } diff --git a/query.go b/query.go index e87fc00..af6d9da 100644 --- a/query.go +++ b/query.go @@ -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 } @@ -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 @@ -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 { @@ -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 @@ -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{}