Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved the "in" support #67

Merged
merged 4 commits into from
Apr 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to do the same for the cockroach driver, since it's a Postgres-compatible DB.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed


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