Merge pull request #17 from sunboyy/query-doc
Create query spec documentation
This commit is contained in:
commit
18f37a3328
6 changed files with 74 additions and 15 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
*.out
|
*.out
|
||||||
|
cover*
|
||||||
|
|
48
README.md
48
README.md
|
@ -10,7 +10,7 @@
|
||||||
<img src="https://api.codeclimate.com/v1/badges/d0270245c28814200c5f/maintainability" />
|
<img src="https://api.codeclimate.com/v1/badges/d0270245c28814200c5f/maintainability" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
Repogen is a code generator for database repository in Golang inspired by Spring Data JPA. (WIP)
|
Repogen is a code generator for database repository in Golang inspired by Spring Data JPA.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -80,7 +80,8 @@ $ repogen -src=<src_file> -dest=<dest_file> -model=<model_struct> -repo=<repo_in
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ repogen -src=examples/getting-started/user.go -dest=examples/getting-started/user_repo.go -model=UserModel -repo=UserRepository
|
$ repogen -src=examples/getting-started/user.go -dest=examples/getting-started/user_repo.go \
|
||||||
|
-model=UserModel -repo=UserRepository
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also write the above command in the `go:generate` format inside Go files in order to generate the implementation when `go generate` command is executed.
|
You can also write the above command in the `go:generate` format inside Go files in order to generate the implementation when `go generate` command is executed.
|
||||||
|
@ -176,6 +177,49 @@ A `Count` operation is also similar to `Find` operation except it has only multi
|
||||||
CountByGender(ctx context.Context, gender Gender) (int, error)
|
CountByGender(ctx context.Context, gender Gender) (int, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Query Specification
|
||||||
|
|
||||||
|
A query can be applied on `Find`, `Update`, `Delete` and `Count` operations. The query specification starts with `By` or `All` word in the method name.
|
||||||
|
|
||||||
|
- `All` is used for querying all documents of the given type in the database. It is simple because only one word `All` is enough for repogen to understand. For example, `FindAll`, `UpdateCityAll` and `DeleteAll`.
|
||||||
|
- `By` is used for querying by a set of fields with specific operators. It is more complicated than `All` query but not be too difficult to understand. For example, `FindByGenderAndCity` and `DeleteByAgeGreaterThan`.
|
||||||
|
|
||||||
|
#### Specifying fields to query
|
||||||
|
|
||||||
|
You can write a query by specifying field name after `By` such as `ByID`. In case that you have multiple fields to query, you can connect field names with `And` or `Or` word such as `ByCityAndGender` and `ByCityOrGender`. `And` and `Or` operators are different in their meaning so the query result will be also different.
|
||||||
|
|
||||||
|
After specifying query to the method name, you also need to provide method parameters that match the given query fields. The example is given below:
|
||||||
|
|
||||||
|
```go
|
||||||
|
FindByCityAndGender(ctx context.Context, city string, gender Gender) ([]*UserModel, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming that the `City` field in the `UserModel` struct is of type `string` and the `Gender` field in the `UserModel` struct is of custom type `Gender`, you have to provide `string` and `Gender` type parameters in the method.
|
||||||
|
|
||||||
|
#### Comparators to each field
|
||||||
|
|
||||||
|
When you specify the query like `ByAge`, it finds documents that contains age value **equal to** the provided parameter value. However, there are other types of comparators provided for you to use as follows.
|
||||||
|
|
||||||
|
- `Not`: The value in the document is not equal to the provided parameter value.
|
||||||
|
- `LessThan`: The value in the document is less than the provided parameter value.
|
||||||
|
- `LessThanEqual`: The value in the document is less than or equal to the provided parameter value.
|
||||||
|
- `GreaterThan`: The value in the document is greater than the provided parameter value.
|
||||||
|
- `GreaterThanEqual`: The value in the document is greater than of equal to the provided parameter value.
|
||||||
|
- `Between`: The value in the document is between two provided parameter values (inclusive).
|
||||||
|
- `In`: The value in the document is in the provided parameter value (which is a slice).
|
||||||
|
|
||||||
|
To apply these comparators to the query, place these words after the field name such as `ByAgeGreaterThan`. You can also use comparators along with `And` and `Or` operators. For example, `ByGenderNotOrAgeLessThan` will apply `Not` comparator to the `Gender` field and `LessThan` comparator to the `Age` field.
|
||||||
|
|
||||||
|
`Between` and `In` comparators are special in terms of parameter requirements. `Between` needs two parameters to perform the query and `In` needs a slice instead of its raw type. The example is provided below:
|
||||||
|
|
||||||
|
```go
|
||||||
|
FindByAgeBetween(ctx context.Context, fromAge int, toAge int) ([]*UserModel, error)
|
||||||
|
|
||||||
|
FindByCityIn(ctx context.Context, cities []string) ([]*UserModel, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming that the `Age` field in the `UserModel` struct is of type `int`, it requires that there must be two `int` parameters provided for `Age` field in the method. And assuming that the `City` field in the `UserModel` struct is of type `string`, it requires that the parameter that is provided to the query must be of slice type.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under [MIT](https://github.com/sunboyy/repogen/blob/main/LICENSE)
|
Licensed under [MIT](https://github.com/sunboyy/repogen/blob/main/LICENSE)
|
||||||
|
|
|
@ -215,8 +215,10 @@ func (r *UserRepositoryMongo) FindByID(arg0 context.Context, arg1 primitive.Obje
|
||||||
|
|
||||||
func (r *UserRepositoryMongo) FindByGenderNotAndAgeLessThan(arg0 context.Context, arg1 Gender, arg2 int) (*UserModel, error) {
|
func (r *UserRepositoryMongo) FindByGenderNotAndAgeLessThan(arg0 context.Context, arg1 Gender, arg2 int) (*UserModel, error) {
|
||||||
cursor, err := r.collection.Find(arg0, bson.M{
|
cursor, err := r.collection.Find(arg0, bson.M{
|
||||||
"gender": bson.M{"$ne": arg1},
|
"$and": []bson.M{
|
||||||
"age": bson.M{"$lt": arg2},
|
{"gender": bson.M{"$ne": arg1}},
|
||||||
|
{"age": bson.M{"$lt": arg2}},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -258,8 +258,10 @@ func (r *UserRepositoryMongo) FindByGender(arg0 context.Context, arg1 Gender) ([
|
||||||
ExpectedCode: `
|
ExpectedCode: `
|
||||||
func (r *UserRepositoryMongo) FindByGenderAndAge(arg0 context.Context, arg1 Gender, arg2 int) ([]*UserModel, error) {
|
func (r *UserRepositoryMongo) FindByGenderAndAge(arg0 context.Context, arg1 Gender, arg2 int) ([]*UserModel, error) {
|
||||||
cursor, err := r.collection.Find(arg0, bson.M{
|
cursor, err := r.collection.Find(arg0, bson.M{
|
||||||
"gender": arg1,
|
"$and": []bson.M{
|
||||||
"age": arg2,
|
{"gender": arg1},
|
||||||
|
{"age": arg2},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -789,8 +791,10 @@ func (r *UserRepositoryMongo) DeleteByGender(arg0 context.Context, arg1 Gender)
|
||||||
ExpectedCode: `
|
ExpectedCode: `
|
||||||
func (r *UserRepositoryMongo) DeleteByGenderAndAge(arg0 context.Context, arg1 Gender, arg2 int) (int, error) {
|
func (r *UserRepositoryMongo) DeleteByGenderAndAge(arg0 context.Context, arg1 Gender, arg2 int) (int, error) {
|
||||||
result, err := r.collection.DeleteMany(arg0, bson.M{
|
result, err := r.collection.DeleteMany(arg0, bson.M{
|
||||||
"gender": arg1,
|
"$and": []bson.M{
|
||||||
"age": arg2,
|
{"gender": arg1},
|
||||||
|
{"age": arg2},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -1149,8 +1153,10 @@ func (r *UserRepositoryMongo) CountByGender(arg0 context.Context, arg1 Gender) (
|
||||||
ExpectedCode: `
|
ExpectedCode: `
|
||||||
func (r *UserRepositoryMongo) CountByGenderAndCity(arg0 context.Context, arg1 Gender, arg2 int) (int, error) {
|
func (r *UserRepositoryMongo) CountByGenderAndCity(arg0 context.Context, arg1 Gender, arg2 int) (int, error) {
|
||||||
count, err := r.collection.CountDocuments(arg0, bson.M{
|
count, err := r.collection.CountDocuments(arg0, bson.M{
|
||||||
"gender": arg1,
|
"$and": []bson.M{
|
||||||
"age": arg2,
|
{"gender": arg1},
|
||||||
|
{"age": arg2},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
@ -31,6 +31,12 @@ func (q querySpec) Code() string {
|
||||||
lines = append(lines, fmt.Sprintf(` {%s},`, predicateCode))
|
lines = append(lines, fmt.Sprintf(` {%s},`, predicateCode))
|
||||||
}
|
}
|
||||||
lines = append(lines, ` },`)
|
lines = append(lines, ` },`)
|
||||||
|
case spec.OperatorAnd:
|
||||||
|
lines = append(lines, ` "$and": []bson.M{`)
|
||||||
|
for _, predicateCode := range predicateCodes {
|
||||||
|
lines = append(lines, fmt.Sprintf(` {%s},`, predicateCode))
|
||||||
|
}
|
||||||
|
lines = append(lines, ` },`)
|
||||||
default:
|
default:
|
||||||
for _, predicateCode := range predicateCodes {
|
for _, predicateCode := range predicateCodes {
|
||||||
lines = append(lines, fmt.Sprintf(` %s,`, predicateCode))
|
lines = append(lines, fmt.Sprintf(` %s,`, predicateCode))
|
||||||
|
|
10
main.go
10
main.go
|
@ -31,6 +31,11 @@ func main() {
|
||||||
panic("-repo flag required")
|
panic("-repo flag required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code, err := generateFromRequest(*sourcePtr, *modelPtr, *repoPtr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
dest := os.Stdout
|
dest := os.Stdout
|
||||||
if *destPtr != "" {
|
if *destPtr != "" {
|
||||||
if err := os.MkdirAll(filepath.Dir(*destPtr), os.ModePerm); err != nil {
|
if err := os.MkdirAll(filepath.Dir(*destPtr), os.ModePerm); err != nil {
|
||||||
|
@ -44,11 +49,6 @@ func main() {
|
||||||
dest = file
|
dest = file
|
||||||
}
|
}
|
||||||
|
|
||||||
code, err := generateFromRequest(*sourcePtr, *modelPtr, *repoPtr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := dest.WriteString(code); err != nil {
|
if _, err := dest.WriteString(code); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue