Skip to content

Commit

Permalink
Improved the "in" support (#67)
Browse files Browse the repository at this point in the history
* Improved the "in" support

* fixed cockroach TranslateSQL
  • Loading branch information
markbates authored Apr 18, 2018
1 parent 97d8d8c commit b591081
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 54 deletions.
77 changes: 54 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ $ go install github.com/gobuffalo/pop/soda

If you're not building your code with `buffalo build`, you'll also have to pass `-tags sqlite` to `go build` when building your program.

## Managing Databases

### Creating Databases

Assuming you defined a configuration file like that described in the above section you can automatically create those databases using the `soda` command:
Expand Down Expand Up @@ -144,7 +146,7 @@ $ soda drop -a
$ soda drop -e development
```

### Models
## Models

The `soda` command supports the generation of models.

Expand All @@ -154,7 +156,7 @@ A full list of commands available for model generation can be found by asking fo
$ soda generate help
```

#### Generate Models
### Generating Models

The `soda` command will generate Go models and, optionally, the associated migrations for you.

Expand Down Expand Up @@ -197,7 +199,7 @@ migrations/20170115024143_create_users.postgres.up.sql
migrations/20170115024143_create_users.postgres.down.sql
```

### Migrations
## Migrations

The `soda` command supports the creation and running of migrations.

Expand All @@ -207,7 +209,7 @@ A full list of commands available for migration can be found by asking for help:
$ soda migrate --help
```

#### Create Migrations
### Generating Migrations

The `soda` command will generate SQL migrations (both the up and down) files for you.

Expand Down Expand Up @@ -239,7 +241,7 @@ Running this command will generate the following files:

The `soda migrate` command supports both `.fizz` and `.sql` files, so you can mix and match them to suit your needs.

#### Running Migrations
### Running Migrations

The `soda` command will run the migrations using the following command:

Expand All @@ -265,25 +267,50 @@ development:
migration_table_name: migrations
```

#### Find
## Querying

### Find By ID

```go
user := models.User{}
err := tx.Find(&user, id)
user := User{}
err := db.Find(&user, id)
```

### Find All

```go
users := []User{}
err := db.All(&users)
err = db.Where("id in (?)", 1, 2, 3).All(&users)
```

#### Query All
### Find Where

```go
tx := models.DB
query := tx.Where("id = 1").Where("name = 'Mark'")
users := []models.User{}
query := db.Where("id = 1").Where("name = 'Mark'")
err := query.All(&users)
err = tx.Where("id in (?)", 1, 2, 3).All(&users)
```

##### Join Query
#### Using `in` Queries

```go
err = db.Where("id in (?)", 1, 2, 3).All(&users)
err = db.Where("id in (?)", 1, 2, 3).Where("foo = ?", "bar").All(&users)
```

Unfortunately, for a variety of reasons you can't use an `and` query in the same `Where` call as an `in` query.

```go
// does not work:
err = db.Where("id in (?) and foo = ?", 1, 2, 3, "bar").All(&users)
// works:
err = db.Where("id in (?)", 1, 2, 3).Where("foo = ?", "bar").All(&users)
```

### Join Query

```go
// page: page number
Expand All @@ -300,7 +327,7 @@ sql, args := query.ToSQL(&pop.Model{Value: models.UserRole{}}, "user_roles.*",
err := models.DB.RawQuery(sql, args...).All(&roles)
```

#### Create
## Creating New Records

```go
// Create one record.
Expand All @@ -317,7 +344,10 @@ users := models.Users{
err := tx.Create(&users)
```

#### Save
## Saving Records

The `Save` method will attempt to create the record if the `ID` is empty. If there is an `ID` set it will attempt to update the record with that ID in the database.

```go
// Save one record.
user := models.User{}
Expand All @@ -333,7 +363,8 @@ users := models.Users{
err := tx.Save(&users)
```

#### Update
## Updating Records

```go
// Update one record.
user := models.User{}
Expand All @@ -356,7 +387,8 @@ users[1].Name = "Larry Morales"
err := tx.Update(&users)
```

#### Destroy
## Destroy

```go
// Destroy one record.
user := models.User{}
Expand All @@ -375,7 +407,7 @@ err := tx.Create(&users)
err = tx.Destroy(&users)
```

### Eager Loading
## Eager Loading

Pop allows you to perform an eager loading for associations defined in a model. By using `pop.Connection.Eager()` function plus some fields tags predefined in your model you can extract associated data from a model.

Expand Down Expand Up @@ -449,7 +481,7 @@ err := tx.Eager().Where("name = 'Mark'").All(&u)
err = tx.Eager("Books").Where("name = 'Mark'").All(&u)
```

#### Eager Loading Nested Associations
### Eager Loading Nested Associations

Pop allows you to eager loading nested associations by using `.` character to concatenate them. Take a look at the example bellow.

Expand All @@ -473,12 +505,11 @@ tx.Eager("Books.Writers.Book").First(&u)
tx.Eager("Books.Writers").Eager("FavoriteSong").First(&u)
```

#### Eager Creation
### Eager Creation

Pop allows you to create models and their associations in one step. You no longer need to create every association separately anymore. Pop will even create join table records for `many_to_many` associations.

Assuming the following pieces of psuedo-code:

Assuming the following pieces of pseudo-code:

```go
user := User{
Expand Down Expand Up @@ -520,7 +551,7 @@ tx.Eager().Create(&book)

All these cases are assuming that none of models and associations has previously been saved in database.

#### Callbacks
## Callbacks

Pop provides a means to execute code before and after database operations. This is done by defining specific methods on your models. For example, to hash a user password you may want to define the following method:

Expand Down Expand Up @@ -555,6 +586,6 @@ func (u *User) BeforeSave(tx *pop.Connection) error {
* AfterDestroy
* AfterFind

#### Further Reading
## Further Reading

[The Unofficial pop Book:](https://andrew-sledge.gitbooks.io/the-unofficial-pop-book/content/) a gentle introduction to new users.
17 changes: 2 additions & 15 deletions cockroach.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
"os"
"os/exec"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -150,20 +149,8 @@ func (p *cockroach) TranslateSQL(sql string) string {
if csql, ok := p.translateCache[sql]; ok {
return csql
}
curr := 1
out := make([]byte, 0, len(sql))
for i := 0; i < len(sql); i++ {
if sql[i] == '?' {
str := "$" + strconv.Itoa(curr)
for _, char := range str {
out = append(out, byte(char))
}
curr++
} else {
out = append(out, sql[i])
}
}
csql := string(out)
csql := sqlx.Rebind(sqlx.DOLLAR, sql)

p.translateCache[sql] = csql
return csql
}
Expand Down
17 changes: 2 additions & 15 deletions postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
"os"
"os/exec"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -153,20 +152,8 @@ func (p *postgresql) TranslateSQL(sql string) string {
if csql, ok := p.translateCache[sql]; ok {
return csql
}
curr := 1
out := make([]byte, 0, len(sql))
for i := 0; i < len(sql); i++ {
if sql[i] == '?' {
str := "$" + strconv.Itoa(curr)
for _, char := range str {
out = append(out, byte(char))
}
curr++
} else {
out = append(out, sql[i])
}
}
csql := string(out)
csql := sqlx.Rebind(sqlx.DOLLAR, sql)

p.translateCache[sql] = csql
return csql
}
Expand Down
13 changes: 12 additions & 1 deletion query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package pop

import "fmt"
import (
"fmt"
"strings"
)

// Query is the main value that is used to build up a query
// to be executed against the `Connection`.
Expand Down Expand Up @@ -95,6 +98,14 @@ func (q *Query) Where(stmt string, args ...interface{}) *Query {
fmt.Println("Warning: Query is setup to use raw SQL")
return q
}
if inRegex.MatchString(stmt) {
var inq []string
for i := 0; i < len(args); i++ {
inq = append(inq, "?")
}
qs := fmt.Sprintf("(%s)", strings.Join(inq, ","))
stmt = strings.Replace(stmt, "(?)", qs, 1)
}
q.whereClauses = append(q.whereClauses, clause{stmt, args})
return q
}
Expand Down
21 changes: 21 additions & 0 deletions query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,27 @@ func Test_Where_In(t *testing.T) {

songs := []Song{}
err = tx.Where("id in (?)", u1.ID, u3.ID).All(&songs)
r.NoError(err)
r.Len(songs, 2)
})
}

func Test_Where_In_Complex(t *testing.T) {
r := require.New(t)
transaction(func(tx *pop.Connection) {
u1 := &Song{Title: "A"}
u2 := &Song{Title: "A"}
u3 := &Song{Title: "A"}
err := tx.Create(u1)
r.NoError(err)
err = tx.Create(u2)
r.NoError(err)
err = tx.Create(u3)
r.NoError(err)

songs := []Song{}
err = tx.Where("id in (?)", u1.ID, u3.ID).Where("title = ?", "A").All(&songs)
r.NoError(err)
r.Len(songs, 2)
})
}
Expand Down

0 comments on commit b591081

Please sign in to comment.