├── .circleci └── config.yml ├── .github └── workflows │ └── cicd.yml ├── .gitignore ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── README.md ├── app.yaml ├── cmd └── qbg │ └── main.go ├── generator.go ├── generator_test.go ├── misc └── fixture │ ├── a │ ├── model.go │ └── model_query.go │ ├── b │ ├── model.go │ └── model_query.go │ ├── c │ ├── model.go │ └── model_query.go │ ├── d │ ├── model.go │ └── model_query.go │ ├── e │ ├── model.go │ ├── model_query.go │ └── model_test.go │ ├── f │ ├── model.go │ ├── model_query.go │ └── model_test.go │ ├── g │ ├── model.go │ └── model_query.go │ ├── h │ ├── model.go │ └── model_query.go │ └── i │ ├── model.go │ └── model_query.go ├── qbgutils └── utils.go ├── setup.sh ├── template.go ├── test.sh ├── usage_test.go └── v2 ├── .gitignore ├── LICENSE ├── README.md ├── app.yaml ├── cmd └── qbg │ └── main.go ├── generator.go ├── generator_test.go ├── go.mod ├── go.sum ├── misc └── fixture │ ├── a │ ├── model.go │ └── model_query.go │ ├── b │ ├── model.go │ └── model_query.go │ ├── c │ ├── model.go │ └── model_query.go │ ├── d │ ├── model.go │ └── model_query.go │ ├── e │ ├── model.go │ ├── model_query.go │ └── model_test.go │ ├── f │ ├── model.go │ └── model_query.go │ ├── g │ ├── model.go │ └── model_query.go │ ├── h │ ├── model.go │ └── model_query.go │ └── i │ ├── model.go │ └── model_query.go ├── qbgutils └── utils.go ├── setup.sh ├── template.go ├── test.sh └── usage_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: /go/src/github.com/favclip/qbg 5 | docker: 6 | - image: google/cloud-sdk:178.0.0 7 | environment: 8 | GOPATH: /go 9 | GOLANG_VERSION: 1.9.2 10 | steps: 11 | - run: 12 | name: PATH update 13 | command: | 14 | echo "export PATH=\$PATH:/go/bin:/usr/local/go/bin:/usr/lib/google-cloud-sdk/platform/google_appengine" >> $BASH_ENV 15 | cat $BASH_ENV 16 | - run: 17 | name: install go binary 18 | command: | 19 | echo $PATH 20 | /usr/bin/curl -o go.tar.gz https://storage.googleapis.com/golang/go${GOLANG_VERSION}.linux-amd64.tar.gz && \ 21 | tar -zxf go.tar.gz && \ 22 | mv go /usr/local && \ 23 | rm go.tar.gz 24 | - run: 25 | name: apply monkey patch 26 | command: chmod +x /usr/lib/google-cloud-sdk/platform/google_appengine/goapp 27 | 28 | - checkout 29 | 30 | - run: go get -u github.com/golang/dep/cmd/dep 31 | - run: ./setup.sh 32 | - run: ./test.sh 33 | -------------------------------------------------------------------------------- /.github/workflows/cicd.yml: -------------------------------------------------------------------------------- 1 | name: 'CI' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request_target: {} 8 | 9 | jobs: 10 | test-v2: 11 | name: Test v2 12 | runs-on: ubuntu-latest 13 | continue-on-error: true 14 | strategy: 15 | matrix: 16 | go_version: 17 | - ~1.11 18 | - ~1.12 19 | - ~1.16 20 | defaults: 21 | run: 22 | working-directory: v2 23 | permissions: 24 | contents: read 25 | env: 26 | GCLOUD_VERSION: "392.0.0" 27 | GO111MODULE: "on" 28 | GOOGLE_CLOUD_PROJECT: "unittest" 29 | steps: 30 | - uses: actions/checkout@v3 31 | with: 32 | # pull_request_target の場合PRのhead(場合によってはvulnerable)、そうじゃなければcontextのsha 33 | ref: ${{ github.event.pull_request.head.sha || github.sha }} 34 | - uses: actions/setup-go@v3 35 | with: 36 | go-version: ${{ matrix.go_version }} 37 | - name: Cache go dependencies 38 | uses: actions/cache@v3 39 | id: cache 40 | with: 41 | key: ${{ runner.os }}-go-cache-r1-v2-${{ matrix.go_version }}-${{ hashFiles('**/go.mod') }}-${{ hashFiles('**/go.sum') }} 42 | path: |- 43 | ~/go/pkg/mod 44 | - name: Prepare dependencies 45 | if: steps.cache.outputs.cache-hit != 'true' 46 | run: |- 47 | ./setup.sh 48 | - uses: google-github-actions/setup-gcloud@v0 49 | with: 50 | version: ${{ env.GCLOUD_VERSION }} 51 | project_id: ${{ env.DATASTORE_PROJECT_ID }} 52 | install_components: "app-engine-go" 53 | - name: Run tests 54 | run: |- 55 | ./test.sh -v -timeout 4m 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .idea/ 4 | *.iml 5 | 6 | vendor/ 7 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | digest = "1:13b41b076f2eccfb9c237f683d226ecf66ddcf663e6172c83d8f61317e672bfc" 6 | name = "github.com/favclip/genbase" 7 | packages = ["."] 8 | pruneopts = "" 9 | revision = "33e3f4f43761221e7ef1f0be35bad3711ae26cbd" 10 | version = "v1.0.0" 11 | 12 | [[projects]] 13 | digest = "1:b1d3041d568e065ab4d76f7477844458e9209c0bb241eaccdc0770bf0a13b120" 14 | name = "github.com/golang/protobuf" 15 | packages = ["proto"] 16 | pruneopts = "" 17 | revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" 18 | version = "v1.1.0" 19 | 20 | [[projects]] 21 | branch = "master" 22 | digest = "1:51cf0d7dfb0def086ee1d36d3bc3ee5cad0a4130be9c144d85f7d1cfd7340ec3" 23 | name = "github.com/mjibson/goon" 24 | packages = ["."] 25 | pruneopts = "" 26 | revision = "0c01b4bc4f4970fa6f91409bf7f134a10809219c" 27 | 28 | [[projects]] 29 | digest = "1:a7e5bd2cebd7a93bb113d3fc414e40203a3f352bde7f5408cc954e0cb53b8b39" 30 | name = "go.mercari.io/datastore" 31 | packages = [ 32 | ".", 33 | "internal", 34 | "internal/c/atomiccache", 35 | "internal/c/fields", 36 | ] 37 | pruneopts = "" 38 | revision = "8f081843d4aadbd447366f05507f14d8e48c5e5f" 39 | version = "v1.0.0" 40 | 41 | [[projects]] 42 | branch = "master" 43 | digest = "1:58d8f8f3ad415b10d2145316519e5b7995b7cf9e663b33a1e9e0c2ddd96c1d58" 44 | name = "golang.org/x/net" 45 | packages = [ 46 | "context", 47 | "context/ctxhttp", 48 | ] 49 | pruneopts = "" 50 | revision = "a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1" 51 | 52 | [[projects]] 53 | branch = "master" 54 | digest = "1:b9e1a35de8b10387d6d0c97c41f393d2025fb51b6f00b8cb9a1a9fca62956884" 55 | name = "golang.org/x/oauth2" 56 | packages = [ 57 | ".", 58 | "internal", 59 | ] 60 | pruneopts = "" 61 | revision = "ef147856a6ddbb60760db74283d2424e98c87bff" 62 | 63 | [[projects]] 64 | digest = "1:eede11c81b63c8f6fd06ef24ba0a640dc077196ec9b7a58ecde03c82eee2f151" 65 | name = "google.golang.org/appengine" 66 | packages = [ 67 | ".", 68 | "aetest", 69 | "datastore", 70 | "internal", 71 | "internal/app_identity", 72 | "internal/base", 73 | "internal/datastore", 74 | "internal/log", 75 | "internal/memcache", 76 | "internal/modules", 77 | "internal/remote_api", 78 | "internal/urlfetch", 79 | "internal/user", 80 | "log", 81 | "memcache", 82 | "urlfetch", 83 | "user", 84 | ] 85 | pruneopts = "" 86 | revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" 87 | version = "v1.1.0" 88 | 89 | [solve-meta] 90 | analyzer-name = "dep" 91 | analyzer-version = 1 92 | input-imports = [ 93 | "github.com/favclip/genbase", 94 | "github.com/mjibson/goon", 95 | "go.mercari.io/datastore", 96 | "google.golang.org/appengine", 97 | "google.golang.org/appengine/aetest", 98 | "google.golang.org/appengine/datastore", 99 | "google.golang.org/appengine/memcache", 100 | ] 101 | solver-name = "gps-cdcl" 102 | solver-version = 1 103 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 2 | # for detailed Gopkg.toml documentation. 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 tv-asahi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qbg 2 | 3 | `Query Builder Generator`. 4 | 5 | ## Description 6 | 7 | `qbg` can generate type safe wrapper for appengine datastore. 8 | 9 | If you use string literal for building query, You shoots your foot when you do typo. 10 | qbg generate appengine datastore wrapper code. It is type safe. Your mistake will be found by go compiler. 11 | 12 | ``` 13 | type User struct { 14 | Name string 15 | } 16 | ``` 17 | 18 | ``` 19 | user := &User { 20 | "go-chan", 21 | } 22 | builder := NewUserQueryBuilder() 23 | builder.Name.Equal("go-chan") 24 | cnt, err := builder.Query().Count(c) 25 | ``` 26 | 27 | ### Example 28 | 29 | [from](https://github.com/favclip/qbg/blob/master/misc/fixture/a/model.go): 30 | 31 | ``` 32 | type Sample struct { 33 | Foo string 34 | } 35 | ``` 36 | 37 | [to](https://github.com/favclip/qbg/blob/master/misc/fixture/a/model_query.go): 38 | 39 | ``` 40 | // generated! 41 | // for Sample 42 | type SampleQueryBuilder struct { 43 | q *datastore.Query 44 | plugin qbgutils.Plugin 45 | Foo *SampleQueryProperty 46 | } 47 | 48 | type SampleQueryProperty struct { 49 | bldr *SampleQueryBuilder 50 | name string 51 | } 52 | ``` 53 | 54 | usage: 55 | 56 | ``` 57 | src := &Sample{"Foo!"} 58 | 59 | builder := NewSampleQueryBuilder() // generated! 60 | builder.Foo.GreaterThanOrEqual("Foo") 61 | cnt, err := builder.Query().Count(c) 62 | ``` 63 | 64 | [other example](https://github.com/favclip/qbg/blob/master/usage_test.go). 65 | 66 | ### With `go generate` 67 | 68 | ``` 69 | $ ls -la . 70 | total 8 71 | drwxr-xr-x@ 3 vvakame staff 102 10 13 17:39 . 72 | drwxr-xr-x@ 7 vvakame staff 238 8 14 18:26 .. 73 | -rw-r--r--@ 1 vvakame staff 178 8 14 18:26 model.go 74 | $ cat model.go 75 | //go:generate qbg -output model_query.go . 76 | 77 | package c 78 | 79 | import "time" 80 | 81 | // +qbg 82 | type Sample struct { 83 | ID int64 `goon:"id"` 84 | CreatedAt time.Time `datastore:",noindex"` 85 | } 86 | $ go generate 87 | $ ls -la . 88 | total 16 89 | drwxr-xr-x@ 4 vvakame staff 136 10 13 17:40 . 90 | drwxr-xr-x@ 7 vvakame staff 238 8 14 18:26 .. 91 | -rw-r--r--@ 1 vvakame staff 178 8 14 18:26 model.go 92 | -rw-r--r-- 1 vvakame staff 3709 10 13 17:40 model_query.go 93 | ``` 94 | 95 | ### Recommend 96 | 97 | Please use with [goon](https://github.com/mjibson/goon). 98 | 99 | ## Installation 100 | 101 | ``` 102 | $ go get -u github.com/favclip/qbg/cmd/qbg 103 | $ qbg 104 | Usage of qbg: 105 | qbg [flags] [directory] 106 | qbg [flags] files... # Must be a single package 107 | Flags: 108 | -output="": output file name; default srcdir/_query.go 109 | -type="": comma-separated list of type names; must be set 110 | ``` 111 | 112 | ## Command sample 113 | 114 | Model with type specific option. 115 | 116 | ``` 117 | $ cat misc/fixture/a/model.go 118 | package a 119 | 120 | // test for basic struct definition 121 | 122 | type Sample struct { 123 | Foo string 124 | } 125 | $ qbg -type Sample -output misc/fixture/a/model_query.go misc/fixture/a 126 | ``` 127 | 128 | Model with tagged comment. 129 | 130 | ``` 131 | $ cat misc/fixture/c/model.go 132 | //go:generate qbg -output model_query.go . 133 | 134 | package c 135 | 136 | import "time" 137 | 138 | // +qbg 139 | type Sample struct { 140 | ID int64 `goon:"id"` 141 | CreatedAt time.Time `datastore:",noindex"` 142 | } 143 | $ qbg -output misc/fixture/d/model_query.go misc/fixture/d 144 | ``` 145 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | runtime: go 2 | api_version: go1.8 3 | 4 | handlers: 5 | - url: /.* 6 | script: _go_app 7 | -------------------------------------------------------------------------------- /cmd/qbg/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/favclip/genbase" 13 | "github.com/favclip/qbg" 14 | ) 15 | 16 | var ( 17 | typeNames = flag.String("type", "", "comma-separated list of type names; must be set") 18 | output = flag.String("output", "", "output file name; default srcdir/_query.go") 19 | private = flag.Bool("private", false, "generated type name; export or unexport") 20 | inlineInterfaces = flag.Bool("inlineinterfaces", false, "generate interfaces to inline; don't use qbgutils") 21 | useDatastoreWrapper = flag.Bool("usedatastorewrapper", false, "use go.mercari.io/datastore") 22 | ) 23 | 24 | // Usage is a replacement usage function for the flags package. 25 | func Usage() { 26 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 27 | fmt.Fprint(os.Stderr, "\tqbg [flags] [directory]\n") 28 | fmt.Fprint(os.Stderr, "\tqbg [flags] files... # Must be a single package\n") 29 | fmt.Fprint(os.Stderr, "Flags:\n") 30 | flag.PrintDefaults() 31 | os.Exit(2) 32 | } 33 | 34 | func main() { 35 | log.SetFlags(0) 36 | log.SetPrefix("qbg: ") 37 | flag.Usage = Usage 38 | flag.Parse() 39 | 40 | // We accept either one directory or a list of files. Which do we have? 41 | args := flag.Args() 42 | if len(args) == 0 { 43 | // Default: process whole package in current directory. 44 | args = []string{"."} 45 | } 46 | 47 | var dir string 48 | var pInfo *genbase.PackageInfo 49 | var err error 50 | p := &genbase.Parser{SkipSemanticsCheck: true} 51 | if len(args) == 1 && isDirectory(args[0]) { 52 | dir = args[0] 53 | pInfo, err = p.ParsePackageDir(dir) 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | } else { 58 | dir = filepath.Dir(args[0]) 59 | pInfo, err = p.ParsePackageFiles(args) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | } 64 | 65 | var typeInfos genbase.TypeInfos 66 | if len(*typeNames) == 0 { 67 | typeInfos = pInfo.CollectTaggedTypeInfos("+qbg") 68 | } else { 69 | typeInfos = pInfo.CollectTypeInfos(strings.Split(*typeNames, ",")) 70 | } 71 | 72 | if len(typeInfos) == 0 { 73 | flag.Usage() 74 | } 75 | 76 | bu := qbg.BuildSource{} 77 | bu.InlineInterfaces = *inlineInterfaces 78 | bu.UseDatastoreWrapper = *useDatastoreWrapper 79 | if bu.UseDatastoreWrapper { 80 | // go.mercari.io/datastore can't use qbgutils 81 | bu.InlineInterfaces = true 82 | } 83 | err = bu.Parse(pInfo, typeInfos) 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | for _, st := range bu.Structs { 88 | st.Private = *private 89 | } 90 | 91 | // Format the output. 92 | src, err := bu.Emit(nil) 93 | if err != nil { 94 | log.Printf("warning: internal error: invalid Go generated: %s", err) 95 | log.Print("warning: compile the package to analyze the error") 96 | } 97 | 98 | // Write to file. 99 | outputName := *output 100 | if outputName == "" { 101 | baseName := fmt.Sprintf("%s_query.go", typeInfos[0].Name()) 102 | outputName = filepath.Join(dir, strings.ToLower(baseName)) 103 | } 104 | err = ioutil.WriteFile(outputName, src, 0644) 105 | if err != nil { 106 | log.Fatalf("writing output: %s", err) 107 | } 108 | } 109 | 110 | // isDirectory reports whether the named file is a directory. 111 | func isDirectory(name string) bool { 112 | info, err := os.Stat(name) 113 | if err != nil { 114 | log.Fatal(err) 115 | } 116 | return info.IsDir() 117 | } 118 | -------------------------------------------------------------------------------- /generator.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "strings" 7 | "text/template" 8 | "unicode" 9 | 10 | "github.com/favclip/genbase" 11 | ) 12 | 13 | // BuildSource represents source code of assembling.. 14 | type BuildSource struct { 15 | g *genbase.Generator 16 | pkg *genbase.PackageInfo 17 | typeInfos genbase.TypeInfos 18 | 19 | InlineInterfaces bool 20 | UseDatastoreWrapper bool 21 | Structs []*BuildStruct 22 | } 23 | 24 | // BuildStruct represents struct of assembling.. 25 | type BuildStruct struct { 26 | parent *BuildSource 27 | typeInfo *genbase.TypeInfo 28 | 29 | Private bool 30 | Fields []*BuildField 31 | } 32 | 33 | // BuildField represents field of BuildStruct. 34 | type BuildField struct { 35 | parent *BuildStruct 36 | fieldInfo *genbase.FieldInfo 37 | 38 | Name string 39 | Tag *BuildTag 40 | } 41 | 42 | // BuildTag represents tag of BuildField. 43 | type BuildTag struct { 44 | field *BuildField 45 | 46 | Kind string // e.g. `goon:"kind,FooKind"` 47 | Name string 48 | PropertyNameAlter string // e.g. `qbg:"StartAt"` 49 | ID bool 50 | Ignore bool // e.g. Secret string `datastore:"-"` 51 | NoIndex bool // e.g. no index `datastore:",noindex"` 52 | } 53 | 54 | // Parse construct *BuildSource from package & type information. 55 | // deprecated. use *BuildSource#Parse instead. 56 | func Parse(pkg *genbase.PackageInfo, typeInfos genbase.TypeInfos) (*BuildSource, error) { 57 | bu := &BuildSource{ 58 | g: genbase.NewGenerator(pkg), 59 | pkg: pkg, 60 | typeInfos: typeInfos, 61 | } 62 | 63 | bu.g.AddImport("google.golang.org/appengine/datastore", "") 64 | bu.g.AddImport("github.com/favclip/qbg/qbgutils", "") 65 | 66 | for _, typeInfo := range typeInfos { 67 | err := bu.parseStruct(typeInfo) 68 | if err != nil { 69 | return nil, err 70 | } 71 | } 72 | 73 | return bu, nil 74 | } 75 | 76 | // Parse construct *BuildSource from package & type information. 77 | func (b *BuildSource) Parse(pkg *genbase.PackageInfo, typeInfos genbase.TypeInfos) error { 78 | if b.g == nil { 79 | b.g = genbase.NewGenerator(pkg) 80 | } 81 | b.pkg = pkg 82 | b.typeInfos = typeInfos 83 | 84 | if b.UseDatastoreWrapper { 85 | b.g.AddImport("go.mercari.io/datastore", "") 86 | } else { 87 | b.g.AddImport("google.golang.org/appengine/datastore", "") 88 | } 89 | if !b.InlineInterfaces { 90 | b.g.AddImport("github.com/favclip/qbg/qbgutils", "") 91 | } 92 | 93 | for _, typeInfo := range typeInfos { 94 | err := b.parseStruct(typeInfo) 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (b *BuildSource) parseStruct(typeInfo *genbase.TypeInfo) error { 104 | structType, err := typeInfo.StructType() 105 | if err != nil { 106 | return err 107 | } 108 | 109 | st := &BuildStruct{ 110 | parent: b, 111 | typeInfo: typeInfo, 112 | } 113 | 114 | for _, fieldInfo := range structType.FieldInfos() { 115 | if len := len(fieldInfo.Names); len == 0 { 116 | // embedded struct in outer struct or multiply field declarations 117 | // https://play.golang.org/p/bcxbdiMyP4 118 | continue 119 | } 120 | 121 | for _, nameIdent := range fieldInfo.Names { 122 | err := b.parseField(st, typeInfo, fieldInfo, nameIdent.Name) 123 | if err != nil { 124 | return err 125 | } 126 | } 127 | } 128 | 129 | b.Structs = append(b.Structs, st) 130 | 131 | return nil 132 | } 133 | 134 | func (b *BuildSource) parseField(st *BuildStruct, typeInfo *genbase.TypeInfo, fieldInfo *genbase.FieldInfo, name string) error { 135 | field := &BuildField{ 136 | parent: st, 137 | fieldInfo: fieldInfo, 138 | Name: name, 139 | } 140 | st.Fields = append(st.Fields, field) 141 | 142 | tag := &BuildTag{ 143 | field: field, 144 | Name: name, 145 | } 146 | field.Tag = tag 147 | 148 | if fieldInfo.Tag != nil { 149 | // remove back quote 150 | tagBody := fieldInfo.Tag.Value[1 : len(fieldInfo.Tag.Value)-1] 151 | tagKeys := genbase.GetKeys(tagBody) 152 | structTag := reflect.StructTag(tagBody) 153 | for _, key := range tagKeys { 154 | if key == "datastore" { 155 | tagText := structTag.Get("datastore") 156 | if tagText == "-" && !tag.ID { 157 | tag.Ignore = true 158 | continue 159 | } 160 | if idx := strings.Index(tagText, ","); idx == -1 { 161 | tag.Name = tagText 162 | } else { 163 | for idx != -1 || tagText != "" { 164 | value := tagText 165 | if idx != -1 { 166 | value = tagText[:idx] 167 | tagText = tagText[idx+1:] 168 | } else { 169 | tagText = tagText[len(value):] 170 | } 171 | idx = strings.Index(tagText, ",") 172 | 173 | if value == "noindex" { 174 | tag.NoIndex = true 175 | } else if value != "" { 176 | tag.Name = value 177 | } 178 | } 179 | } 180 | } else if key == "goon" || key == "boom" { 181 | tagText := structTag.Get("goon") 182 | if tagText == "" { 183 | tagText = structTag.Get("boom") 184 | } 185 | if tagText == "id" { 186 | tag.ID = true 187 | tag.Ignore = false 188 | } else if strings.HasPrefix(tagText, "kind,") { 189 | tag.Kind = tagText[5:] 190 | } 191 | } else if key == "qbg" { 192 | tag.PropertyNameAlter = structTag.Get("qbg") 193 | } 194 | } 195 | } 196 | if tag.PropertyNameAlter == "" { 197 | switch field.Name { 198 | case "Ancestor", "KeysOnly", "Start", "Offset", "Limit", "Query": 199 | tag.PropertyNameAlter = field.Name + "Property" 200 | default: 201 | tag.PropertyNameAlter = field.Name 202 | } 203 | } 204 | 205 | return nil 206 | } 207 | 208 | // Emit generate wrapper code. 209 | func (b *BuildSource) Emit(args *[]string) ([]byte, error) { 210 | b.g.PrintHeader("qbg", args) 211 | 212 | if b.InlineInterfaces { 213 | tmpl := template.New("plugin") 214 | tmpl, err := tmpl.Parse(pluginTemplate) 215 | if err != nil { 216 | return nil, err 217 | } 218 | 219 | var dsKeyType string 220 | if b.UseDatastoreWrapper { 221 | dsKeyType = "datastore.Key" 222 | } else { 223 | dsKeyType = "*datastore.Key" 224 | } 225 | 226 | buf := bytes.NewBufferString("") 227 | err = tmpl.Execute(buf, map[string]string{ 228 | "DSKeyType": dsKeyType, 229 | }) 230 | if err != nil { 231 | return nil, err 232 | } 233 | 234 | b.g.Printf(buf.String()) 235 | } 236 | 237 | for _, st := range b.Structs { 238 | err := st.emit(b.g) 239 | if err != nil { 240 | return nil, err 241 | } 242 | } 243 | 244 | return b.g.Format() 245 | } 246 | 247 | func (st *BuildStruct) emit(g *genbase.Generator) error { 248 | tmpl := template.New("struct") 249 | tmpl, err := tmpl.Parse(structTemplate) 250 | if err != nil { 251 | return err 252 | } 253 | 254 | var newWord string 255 | if st.Private { 256 | newWord = "new" 257 | } else { 258 | newWord = "New" 259 | } 260 | var pluginType string 261 | if st.parent.InlineInterfaces { 262 | pluginType = "Plugin" 263 | } else { 264 | pluginType = "qbgutils.Plugin" 265 | } 266 | var pluggerType string 267 | if st.parent.InlineInterfaces { 268 | pluggerType = "Plugger" 269 | } else { 270 | pluggerType = "qbgutils.Plugger" 271 | } 272 | var fields []*BuildField 273 | for _, f := range st.Fields { 274 | if f.Tag.Ignore { 275 | continue 276 | } 277 | fields = append(fields, f) 278 | } 279 | var dsKeyType string 280 | var dsQueryType string 281 | var dsNewQuery string 282 | if st.parent.UseDatastoreWrapper { 283 | dsKeyType = "datastore.Key" 284 | dsQueryType = "datastore.Query" 285 | dsNewQuery = "client.NewQuery" 286 | } else { 287 | dsKeyType = "*datastore.Key" 288 | dsQueryType = "*datastore.Query" 289 | dsNewQuery = "datastore.NewQuery" 290 | } 291 | buf := bytes.NewBufferString("") 292 | err = tmpl.Execute(buf, map[string]interface{}{ 293 | "UserDatastoreWrapper": st.parent.UseDatastoreWrapper, 294 | "Name": st.Name(), 295 | "SimpleName": st.SimpleName(), 296 | "Kind": st.Kind(), 297 | "NewWord": newWord, 298 | "DSKeyType": dsKeyType, 299 | "DSQueryType": dsQueryType, 300 | "DSNewQuery": dsNewQuery, 301 | "PluginType": pluginType, 302 | "PluggerType": pluggerType, 303 | "Fields": fields, 304 | }) 305 | if err != nil { 306 | return err 307 | } 308 | 309 | g.Printf(buf.String()) 310 | 311 | return nil 312 | } 313 | 314 | // Name returns struct type name. 315 | func (st *BuildStruct) Name() string { 316 | // FooBar -> fooBar 317 | // IIS -> iis 318 | // AEData -> aeData 319 | 320 | name := st.SimpleName() 321 | 322 | if !st.Private { 323 | return name 324 | } 325 | if len(name) <= 1 { 326 | return strings.ToLower(name) 327 | } 328 | if name == strings.ToUpper(name) { 329 | return strings.ToLower(name) 330 | } 331 | 332 | runeNames := []rune(name) 333 | if unicode.IsLower(runeNames[0]) { 334 | return name 335 | } else if unicode.IsLower(runeNames[1]) { 336 | return string(unicode.ToLower(runeNames[0])) + string(runeNames[1:]) 337 | } 338 | 339 | var idx int 340 | for idx = 0; idx < len(runeNames); idx++ { 341 | r := runeNames[idx] 342 | if unicode.IsLower(r) { 343 | break 344 | } 345 | } 346 | 347 | return strings.ToLower(string(runeNames[0:idx-1])) + string(runeNames[idx-1:]) 348 | } 349 | 350 | // Name returns struct type name. 351 | func (st *BuildStruct) SimpleName() string { 352 | return st.typeInfo.Name() 353 | } 354 | 355 | // Kind returns kind from struct. 356 | func (st *BuildStruct) Kind() string { 357 | for _, field := range st.Fields { 358 | if field.Tag.Kind != "" { 359 | return field.Tag.Kind 360 | } 361 | } 362 | return st.Name() 363 | } 364 | -------------------------------------------------------------------------------- /generator_test.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | 7 | "github.com/favclip/genbase" 8 | ) 9 | 10 | func TestGeneratorParsePackageDir(t *testing.T) { 11 | 12 | p := &genbase.Parser{SkipSemanticsCheck: true} 13 | pInfo, err := p.ParsePackageDir("./misc/fixture/a") 14 | if err != nil { 15 | t.Log(err) 16 | t.Fail() 17 | } 18 | 19 | if pInfo.Name() != "a" { 20 | t.Log("package name is not a, actual", pInfo.Name()) 21 | t.Fail() 22 | } 23 | if len(pInfo.Files) != 2 { 24 | t.Log("package files length is not 2, actual", len(pInfo.Files)) 25 | t.Fail() 26 | } 27 | if pInfo.Dir != "./misc/fixture/a" { 28 | t.Log("package dir is not ./misc/fixture/a, actual", pInfo.Dir) 29 | t.Fail() 30 | } 31 | } 32 | 33 | func TestGeneratorParsePackageFiles(t *testing.T) { 34 | p := &genbase.Parser{SkipSemanticsCheck: true} 35 | pInfo, err := p.ParsePackageFiles([]string{"./misc/fixture/a/model.go"}) 36 | if err != nil { 37 | t.Log(err) 38 | t.Fail() 39 | } 40 | 41 | if pInfo.Name() != "a" { 42 | t.Log("package name is not a, actual", pInfo.Name()) 43 | t.Fail() 44 | } 45 | if len(pInfo.Files) != 1 { 46 | t.Log("package files length is not 1, actual", len(pInfo.Files)) 47 | t.Fail() 48 | } 49 | if pInfo.Dir != "." { 50 | t.Log("package dir is not ., actual", pInfo.Dir) 51 | t.Fail() 52 | } 53 | } 54 | 55 | func TestGeneratorGenerate(t *testing.T) { 56 | 57 | testCase := []string{"a", "b"} 58 | for _, postFix := range testCase { 59 | p := &genbase.Parser{SkipSemanticsCheck: true} 60 | pInfo, err := p.ParsePackageDir("./misc/fixture/" + postFix) 61 | if err != nil { 62 | t.Log(err) 63 | t.Fail() 64 | } 65 | args := []string{"-type", "Sample", "-output", "misc/fixture/" + postFix + "/model_query.go", "misc/fixture/" + postFix} 66 | typeNames := []string{"Sample"} 67 | if postFix == "g" { 68 | args = []string{"-type", "Sample,Inner", "-output", "misc/fixture/" + postFix + "/model_query.go", "misc/fixture/" + postFix} 69 | typeNames = []string{"Sample", "Inner"} 70 | } 71 | 72 | bu, err := Parse(pInfo, pInfo.CollectTypeInfos(typeNames)) 73 | if err != nil { 74 | t.Log(err) 75 | t.Fail() 76 | } 77 | src, err := bu.Emit(&args) 78 | if err != nil { 79 | t.Log(err) 80 | t.Fail() 81 | } 82 | expected, err := ioutil.ReadFile("./misc/fixture/" + postFix + "/model_query.go") 83 | if err != nil { 84 | t.Log(err) 85 | t.Fail() 86 | } 87 | if string(src) != string(expected) { 88 | t.Log("not emit expected code in "+postFix+", actual\n", string(src)) 89 | t.Fail() 90 | } 91 | } 92 | } 93 | 94 | func TestCollectTaggedTypes(t *testing.T) { 95 | p := &genbase.Parser{SkipSemanticsCheck: true} 96 | pInfo, err := p.ParsePackageFiles([]string{"./misc/fixture/c/model.go"}) 97 | if err != nil { 98 | t.Log(err) 99 | t.Fail() 100 | } 101 | 102 | names := pInfo.CollectTaggedTypeInfos("+qbg") 103 | if len(names) != 1 { 104 | t.Log("names length is not 1, actual", len(names)) 105 | t.Fail() 106 | } 107 | if names[0].Name() != "Sample" { 108 | t.Log("name[0] is not \"Sample\", actual", names[0].Name()) 109 | t.Fail() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /misc/fixture/a/model.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | // test for basic struct definition 4 | 5 | type Sample struct { 6 | Foo string 7 | } 8 | -------------------------------------------------------------------------------- /misc/fixture/a/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -type Sample -output misc/fixture/a/model_query.go misc/fixture/a; DO NOT EDIT 2 | 3 | package a 4 | 5 | import ( 6 | "github.com/favclip/qbg/qbgutils" 7 | "google.golang.org/appengine/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Foo *SampleQueryProperty 15 | } 16 | 17 | // SampleQueryProperty has property information for SampleQueryBuilder. 18 | type SampleQueryProperty struct { 19 | bldr *SampleQueryBuilder 20 | name string 21 | } 22 | 23 | // NewSampleQueryBuilder create new SampleQueryBuilder. 24 | func NewSampleQueryBuilder() *SampleQueryBuilder { 25 | return NewSampleQueryBuilderWithKind("Sample") 26 | } 27 | 28 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 29 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 30 | q := datastore.NewQuery(kind) 31 | bldr := &SampleQueryBuilder{q: q} 32 | bldr.Foo = &SampleQueryProperty{ 33 | bldr: bldr, 34 | name: "Foo", 35 | } 36 | 37 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 38 | bldr.plugin = plugger.Plugin() 39 | bldr.plugin.Init("Sample") 40 | } 41 | 42 | return bldr 43 | } 44 | 45 | // Ancestor sets parent key to ancestor query. 46 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 47 | bldr.q = bldr.q.Ancestor(parentKey) 48 | if bldr.plugin != nil { 49 | bldr.plugin.Ancestor(parentKey) 50 | } 51 | return bldr 52 | } 53 | 54 | // KeysOnly sets keys only option to query. 55 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 56 | bldr.q = bldr.q.KeysOnly() 57 | if bldr.plugin != nil { 58 | bldr.plugin.KeysOnly() 59 | } 60 | return bldr 61 | } 62 | 63 | // Start setup to query. 64 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 65 | bldr.q = bldr.q.Start(cur) 66 | if bldr.plugin != nil { 67 | bldr.plugin.Start(cur) 68 | } 69 | return bldr 70 | } 71 | 72 | // Offset setup to query. 73 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 74 | bldr.q = bldr.q.Offset(offset) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Offset(offset) 77 | } 78 | return bldr 79 | } 80 | 81 | // Limit setup to query. 82 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 83 | bldr.q = bldr.q.Limit(limit) 84 | if bldr.plugin != nil { 85 | bldr.plugin.Limit(limit) 86 | } 87 | return bldr 88 | } 89 | 90 | // Query returns *datastore.Query. 91 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 92 | return bldr.q 93 | } 94 | 95 | // Filter with op & value. 96 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 97 | switch op { 98 | case "<=": 99 | p.LessThanOrEqual(value) 100 | case ">=": 101 | p.GreaterThanOrEqual(value) 102 | case "<": 103 | p.LessThan(value) 104 | case ">": 105 | p.GreaterThan(value) 106 | case "=": 107 | p.Equal(value) 108 | default: 109 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 110 | } 111 | if p.bldr.plugin != nil { 112 | p.bldr.plugin.Filter(p.name, op, value) 113 | } 114 | return p.bldr 115 | } 116 | 117 | // LessThanOrEqual filter with value. 118 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 119 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 120 | if p.bldr.plugin != nil { 121 | p.bldr.plugin.Filter(p.name, "<=", value) 122 | } 123 | return p.bldr 124 | } 125 | 126 | // GreaterThanOrEqual filter with value. 127 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 128 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 129 | if p.bldr.plugin != nil { 130 | p.bldr.plugin.Filter(p.name, ">=", value) 131 | } 132 | return p.bldr 133 | } 134 | 135 | // LessThan filter with value. 136 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 137 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, "<", value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // GreaterThan filter with value. 145 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, ">", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // Equal filter with value. 154 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, "=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // Asc order. 163 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 164 | p.bldr.q = p.bldr.q.Order(p.name) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Asc(p.name) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // Desc order. 172 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 173 | p.bldr.q = p.bldr.q.Order("-" + p.name) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Desc(p.name) 176 | } 177 | return p.bldr 178 | } 179 | -------------------------------------------------------------------------------- /misc/fixture/b/model.go: -------------------------------------------------------------------------------- 1 | package b 2 | 3 | import "time" 4 | 5 | type Sample struct { 6 | ID int64 `datastore:"-" goon:"id"` 7 | CreatedAt time.Time `datastore:",noindex"` 8 | } 9 | -------------------------------------------------------------------------------- /misc/fixture/b/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -type Sample -output misc/fixture/b/model_query.go misc/fixture/b; DO NOT EDIT 2 | 3 | package b 4 | 5 | import ( 6 | "github.com/favclip/qbg/qbgutils" 7 | "google.golang.org/appengine/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | ID *SampleQueryProperty 15 | CreatedAt *SampleQueryProperty 16 | } 17 | 18 | // SampleQueryProperty has property information for SampleQueryBuilder. 19 | type SampleQueryProperty struct { 20 | bldr *SampleQueryBuilder 21 | name string 22 | } 23 | 24 | // NewSampleQueryBuilder create new SampleQueryBuilder. 25 | func NewSampleQueryBuilder() *SampleQueryBuilder { 26 | return NewSampleQueryBuilderWithKind("Sample") 27 | } 28 | 29 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 30 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 31 | q := datastore.NewQuery(kind) 32 | bldr := &SampleQueryBuilder{q: q} 33 | bldr.ID = &SampleQueryProperty{ 34 | bldr: bldr, 35 | name: "__key__", 36 | } 37 | bldr.CreatedAt = &SampleQueryProperty{ 38 | bldr: bldr, 39 | name: "CreatedAt", 40 | } 41 | 42 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 43 | bldr.plugin = plugger.Plugin() 44 | bldr.plugin.Init("Sample") 45 | } 46 | 47 | return bldr 48 | } 49 | 50 | // Ancestor sets parent key to ancestor query. 51 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 52 | bldr.q = bldr.q.Ancestor(parentKey) 53 | if bldr.plugin != nil { 54 | bldr.plugin.Ancestor(parentKey) 55 | } 56 | return bldr 57 | } 58 | 59 | // KeysOnly sets keys only option to query. 60 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 61 | bldr.q = bldr.q.KeysOnly() 62 | if bldr.plugin != nil { 63 | bldr.plugin.KeysOnly() 64 | } 65 | return bldr 66 | } 67 | 68 | // Start setup to query. 69 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 70 | bldr.q = bldr.q.Start(cur) 71 | if bldr.plugin != nil { 72 | bldr.plugin.Start(cur) 73 | } 74 | return bldr 75 | } 76 | 77 | // Offset setup to query. 78 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 79 | bldr.q = bldr.q.Offset(offset) 80 | if bldr.plugin != nil { 81 | bldr.plugin.Offset(offset) 82 | } 83 | return bldr 84 | } 85 | 86 | // Limit setup to query. 87 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 88 | bldr.q = bldr.q.Limit(limit) 89 | if bldr.plugin != nil { 90 | bldr.plugin.Limit(limit) 91 | } 92 | return bldr 93 | } 94 | 95 | // Query returns *datastore.Query. 96 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 97 | return bldr.q 98 | } 99 | 100 | // Filter with op & value. 101 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 102 | switch op { 103 | case "<=": 104 | p.LessThanOrEqual(value) 105 | case ">=": 106 | p.GreaterThanOrEqual(value) 107 | case "<": 108 | p.LessThan(value) 109 | case ">": 110 | p.GreaterThan(value) 111 | case "=": 112 | p.Equal(value) 113 | default: 114 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 115 | } 116 | if p.bldr.plugin != nil { 117 | p.bldr.plugin.Filter(p.name, op, value) 118 | } 119 | return p.bldr 120 | } 121 | 122 | // LessThanOrEqual filter with value. 123 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 124 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 125 | if p.bldr.plugin != nil { 126 | p.bldr.plugin.Filter(p.name, "<=", value) 127 | } 128 | return p.bldr 129 | } 130 | 131 | // GreaterThanOrEqual filter with value. 132 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 133 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 134 | if p.bldr.plugin != nil { 135 | p.bldr.plugin.Filter(p.name, ">=", value) 136 | } 137 | return p.bldr 138 | } 139 | 140 | // LessThan filter with value. 141 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 142 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 143 | if p.bldr.plugin != nil { 144 | p.bldr.plugin.Filter(p.name, "<", value) 145 | } 146 | return p.bldr 147 | } 148 | 149 | // GreaterThan filter with value. 150 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 151 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 152 | if p.bldr.plugin != nil { 153 | p.bldr.plugin.Filter(p.name, ">", value) 154 | } 155 | return p.bldr 156 | } 157 | 158 | // Equal filter with value. 159 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 160 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 161 | if p.bldr.plugin != nil { 162 | p.bldr.plugin.Filter(p.name, "=", value) 163 | } 164 | return p.bldr 165 | } 166 | 167 | // Asc order. 168 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 169 | p.bldr.q = p.bldr.q.Order(p.name) 170 | if p.bldr.plugin != nil { 171 | p.bldr.plugin.Asc(p.name) 172 | } 173 | return p.bldr 174 | } 175 | 176 | // Desc order. 177 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 178 | p.bldr.q = p.bldr.q.Order("-" + p.name) 179 | if p.bldr.plugin != nil { 180 | p.bldr.plugin.Desc(p.name) 181 | } 182 | return p.bldr 183 | } 184 | -------------------------------------------------------------------------------- /misc/fixture/c/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go . 2 | 3 | package c 4 | 5 | import "time" 6 | 7 | // +qbg 8 | type Sample struct { 9 | ID int64 `goon:"id"` 10 | CreatedAt time.Time `datastore:",noindex"` 11 | } 12 | -------------------------------------------------------------------------------- /misc/fixture/c/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -output misc/fixture/c/model_query.go misc/fixture/c; DO NOT EDIT 2 | 3 | package c 4 | 5 | import ( 6 | "github.com/favclip/qbg/qbgutils" 7 | "google.golang.org/appengine/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | ID *SampleQueryProperty 15 | CreatedAt *SampleQueryProperty 16 | } 17 | 18 | // SampleQueryProperty has property information for SampleQueryBuilder. 19 | type SampleQueryProperty struct { 20 | bldr *SampleQueryBuilder 21 | name string 22 | } 23 | 24 | // NewSampleQueryBuilder create new SampleQueryBuilder. 25 | func NewSampleQueryBuilder() *SampleQueryBuilder { 26 | return NewSampleQueryBuilderWithKind("Sample") 27 | } 28 | 29 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 30 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 31 | q := datastore.NewQuery(kind) 32 | bldr := &SampleQueryBuilder{q: q} 33 | bldr.ID = &SampleQueryProperty{ 34 | bldr: bldr, 35 | name: "__key__", 36 | } 37 | bldr.CreatedAt = &SampleQueryProperty{ 38 | bldr: bldr, 39 | name: "CreatedAt", 40 | } 41 | 42 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 43 | bldr.plugin = plugger.Plugin() 44 | bldr.plugin.Init("Sample") 45 | } 46 | 47 | return bldr 48 | } 49 | 50 | // Ancestor sets parent key to ancestor query. 51 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 52 | bldr.q = bldr.q.Ancestor(parentKey) 53 | if bldr.plugin != nil { 54 | bldr.plugin.Ancestor(parentKey) 55 | } 56 | return bldr 57 | } 58 | 59 | // KeysOnly sets keys only option to query. 60 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 61 | bldr.q = bldr.q.KeysOnly() 62 | if bldr.plugin != nil { 63 | bldr.plugin.KeysOnly() 64 | } 65 | return bldr 66 | } 67 | 68 | // Start setup to query. 69 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 70 | bldr.q = bldr.q.Start(cur) 71 | if bldr.plugin != nil { 72 | bldr.plugin.Start(cur) 73 | } 74 | return bldr 75 | } 76 | 77 | // Offset setup to query. 78 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 79 | bldr.q = bldr.q.Offset(offset) 80 | if bldr.plugin != nil { 81 | bldr.plugin.Offset(offset) 82 | } 83 | return bldr 84 | } 85 | 86 | // Limit setup to query. 87 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 88 | bldr.q = bldr.q.Limit(limit) 89 | if bldr.plugin != nil { 90 | bldr.plugin.Limit(limit) 91 | } 92 | return bldr 93 | } 94 | 95 | // Query returns *datastore.Query. 96 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 97 | return bldr.q 98 | } 99 | 100 | // Filter with op & value. 101 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 102 | switch op { 103 | case "<=": 104 | p.LessThanOrEqual(value) 105 | case ">=": 106 | p.GreaterThanOrEqual(value) 107 | case "<": 108 | p.LessThan(value) 109 | case ">": 110 | p.GreaterThan(value) 111 | case "=": 112 | p.Equal(value) 113 | default: 114 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 115 | } 116 | if p.bldr.plugin != nil { 117 | p.bldr.plugin.Filter(p.name, op, value) 118 | } 119 | return p.bldr 120 | } 121 | 122 | // LessThanOrEqual filter with value. 123 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 124 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 125 | if p.bldr.plugin != nil { 126 | p.bldr.plugin.Filter(p.name, "<=", value) 127 | } 128 | return p.bldr 129 | } 130 | 131 | // GreaterThanOrEqual filter with value. 132 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 133 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 134 | if p.bldr.plugin != nil { 135 | p.bldr.plugin.Filter(p.name, ">=", value) 136 | } 137 | return p.bldr 138 | } 139 | 140 | // LessThan filter with value. 141 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 142 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 143 | if p.bldr.plugin != nil { 144 | p.bldr.plugin.Filter(p.name, "<", value) 145 | } 146 | return p.bldr 147 | } 148 | 149 | // GreaterThan filter with value. 150 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 151 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 152 | if p.bldr.plugin != nil { 153 | p.bldr.plugin.Filter(p.name, ">", value) 154 | } 155 | return p.bldr 156 | } 157 | 158 | // Equal filter with value. 159 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 160 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 161 | if p.bldr.plugin != nil { 162 | p.bldr.plugin.Filter(p.name, "=", value) 163 | } 164 | return p.bldr 165 | } 166 | 167 | // Asc order. 168 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 169 | p.bldr.q = p.bldr.q.Order(p.name) 170 | if p.bldr.plugin != nil { 171 | p.bldr.plugin.Asc(p.name) 172 | } 173 | return p.bldr 174 | } 175 | 176 | // Desc order. 177 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 178 | p.bldr.q = p.bldr.q.Order("-" + p.name) 179 | if p.bldr.plugin != nil { 180 | p.bldr.plugin.Desc(p.name) 181 | } 182 | return p.bldr 183 | } 184 | -------------------------------------------------------------------------------- /misc/fixture/d/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go . 2 | 3 | package c 4 | 5 | import "time" 6 | 7 | // +qbg 8 | type Sample struct { 9 | ID int64 `goon:"id"` 10 | FooUrl string `datastore:"FooURL,noindex"` 11 | Start time.Ticker `qbg:"StartAt"` 12 | Limit int 13 | CreatedAt time.Time `datastore:",noindex"` 14 | } 15 | -------------------------------------------------------------------------------- /misc/fixture/d/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -output misc/fixture/d/model_query.go misc/fixture/d; DO NOT EDIT 2 | 3 | package c 4 | 5 | import ( 6 | "github.com/favclip/qbg/qbgutils" 7 | "google.golang.org/appengine/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | ID *SampleQueryProperty 15 | FooUrl *SampleQueryProperty 16 | StartAt *SampleQueryProperty 17 | LimitProperty *SampleQueryProperty 18 | CreatedAt *SampleQueryProperty 19 | } 20 | 21 | // SampleQueryProperty has property information for SampleQueryBuilder. 22 | type SampleQueryProperty struct { 23 | bldr *SampleQueryBuilder 24 | name string 25 | } 26 | 27 | // NewSampleQueryBuilder create new SampleQueryBuilder. 28 | func NewSampleQueryBuilder() *SampleQueryBuilder { 29 | return NewSampleQueryBuilderWithKind("Sample") 30 | } 31 | 32 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 33 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 34 | q := datastore.NewQuery(kind) 35 | bldr := &SampleQueryBuilder{q: q} 36 | bldr.ID = &SampleQueryProperty{ 37 | bldr: bldr, 38 | name: "__key__", 39 | } 40 | bldr.FooUrl = &SampleQueryProperty{ 41 | bldr: bldr, 42 | name: "FooURL", 43 | } 44 | bldr.StartAt = &SampleQueryProperty{ 45 | bldr: bldr, 46 | name: "Start", 47 | } 48 | bldr.LimitProperty = &SampleQueryProperty{ 49 | bldr: bldr, 50 | name: "Limit", 51 | } 52 | bldr.CreatedAt = &SampleQueryProperty{ 53 | bldr: bldr, 54 | name: "CreatedAt", 55 | } 56 | 57 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 58 | bldr.plugin = plugger.Plugin() 59 | bldr.plugin.Init("Sample") 60 | } 61 | 62 | return bldr 63 | } 64 | 65 | // Ancestor sets parent key to ancestor query. 66 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 67 | bldr.q = bldr.q.Ancestor(parentKey) 68 | if bldr.plugin != nil { 69 | bldr.plugin.Ancestor(parentKey) 70 | } 71 | return bldr 72 | } 73 | 74 | // KeysOnly sets keys only option to query. 75 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 76 | bldr.q = bldr.q.KeysOnly() 77 | if bldr.plugin != nil { 78 | bldr.plugin.KeysOnly() 79 | } 80 | return bldr 81 | } 82 | 83 | // Start setup to query. 84 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 85 | bldr.q = bldr.q.Start(cur) 86 | if bldr.plugin != nil { 87 | bldr.plugin.Start(cur) 88 | } 89 | return bldr 90 | } 91 | 92 | // Offset setup to query. 93 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 94 | bldr.q = bldr.q.Offset(offset) 95 | if bldr.plugin != nil { 96 | bldr.plugin.Offset(offset) 97 | } 98 | return bldr 99 | } 100 | 101 | // Limit setup to query. 102 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 103 | bldr.q = bldr.q.Limit(limit) 104 | if bldr.plugin != nil { 105 | bldr.plugin.Limit(limit) 106 | } 107 | return bldr 108 | } 109 | 110 | // Query returns *datastore.Query. 111 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 112 | return bldr.q 113 | } 114 | 115 | // Filter with op & value. 116 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 117 | switch op { 118 | case "<=": 119 | p.LessThanOrEqual(value) 120 | case ">=": 121 | p.GreaterThanOrEqual(value) 122 | case "<": 123 | p.LessThan(value) 124 | case ">": 125 | p.GreaterThan(value) 126 | case "=": 127 | p.Equal(value) 128 | default: 129 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 130 | } 131 | if p.bldr.plugin != nil { 132 | p.bldr.plugin.Filter(p.name, op, value) 133 | } 134 | return p.bldr 135 | } 136 | 137 | // LessThanOrEqual filter with value. 138 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 139 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 140 | if p.bldr.plugin != nil { 141 | p.bldr.plugin.Filter(p.name, "<=", value) 142 | } 143 | return p.bldr 144 | } 145 | 146 | // GreaterThanOrEqual filter with value. 147 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 148 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 149 | if p.bldr.plugin != nil { 150 | p.bldr.plugin.Filter(p.name, ">=", value) 151 | } 152 | return p.bldr 153 | } 154 | 155 | // LessThan filter with value. 156 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 157 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 158 | if p.bldr.plugin != nil { 159 | p.bldr.plugin.Filter(p.name, "<", value) 160 | } 161 | return p.bldr 162 | } 163 | 164 | // GreaterThan filter with value. 165 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 166 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 167 | if p.bldr.plugin != nil { 168 | p.bldr.plugin.Filter(p.name, ">", value) 169 | } 170 | return p.bldr 171 | } 172 | 173 | // Equal filter with value. 174 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 175 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 176 | if p.bldr.plugin != nil { 177 | p.bldr.plugin.Filter(p.name, "=", value) 178 | } 179 | return p.bldr 180 | } 181 | 182 | // Asc order. 183 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 184 | p.bldr.q = p.bldr.q.Order(p.name) 185 | if p.bldr.plugin != nil { 186 | p.bldr.plugin.Asc(p.name) 187 | } 188 | return p.bldr 189 | } 190 | 191 | // Desc order. 192 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 193 | p.bldr.q = p.bldr.q.Order("-" + p.name) 194 | if p.bldr.plugin != nil { 195 | p.bldr.plugin.Desc(p.name) 196 | } 197 | return p.bldr 198 | } 199 | -------------------------------------------------------------------------------- /misc/fixture/e/model.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/favclip/qbg/qbgutils" 8 | "google.golang.org/appengine/datastore" 9 | ) 10 | 11 | // +qbg 12 | type Sample struct { 13 | Foo string 14 | } 15 | 16 | func (bldr *SampleQueryBuilder) Plugin() qbgutils.Plugin { 17 | if bldr.plugin == nil { 18 | bldr.plugin = &MemcacheQueryPlugin{} 19 | } 20 | return bldr.plugin 21 | } 22 | 23 | type MemcacheQueryPlugin struct { 24 | buf bytes.Buffer 25 | } 26 | 27 | func (p *MemcacheQueryPlugin) AddCounter(counter uint64) { 28 | p.buf.WriteString(fmt.Sprintf(":%d", counter)) 29 | } 30 | 31 | func (p *MemcacheQueryPlugin) Init(typeName string) { 32 | p.buf.WriteString(fmt.Sprintf("k=%s", typeName)) 33 | } 34 | 35 | func (p *MemcacheQueryPlugin) Ancestor(ancestor *datastore.Key) { 36 | p.buf.WriteString(fmt.Sprintf(":!a=%s", ancestor.String())) 37 | } 38 | 39 | func (p *MemcacheQueryPlugin) KeysOnly() { 40 | p.buf.WriteString(":!k") 41 | } 42 | 43 | func (p *MemcacheQueryPlugin) Start(cur datastore.Cursor) { 44 | p.buf.WriteString(fmt.Sprintf(":!s=%s", cur.String())) 45 | } 46 | 47 | func (p *MemcacheQueryPlugin) Offset(offset int) { 48 | p.buf.WriteString(fmt.Sprintf(":!o=%d", offset)) 49 | } 50 | 51 | func (p *MemcacheQueryPlugin) Limit(limit int) { 52 | p.buf.WriteString(fmt.Sprintf(":!l=%d", limit)) 53 | } 54 | 55 | func (p *MemcacheQueryPlugin) Filter(name, op string, value interface{}) { 56 | p.buf.WriteString(fmt.Sprintf(":?%s%s%#v", name, op, value)) 57 | } 58 | 59 | func (p *MemcacheQueryPlugin) Asc(name string) { 60 | p.buf.WriteString(fmt.Sprintf(":A=%s", name)) 61 | } 62 | 63 | func (p *MemcacheQueryPlugin) Desc(name string) { 64 | p.buf.WriteString(fmt.Sprintf(":D=%s", name)) 65 | } 66 | 67 | func (p *MemcacheQueryPlugin) QueryString() string { 68 | return p.buf.String() 69 | } 70 | -------------------------------------------------------------------------------- /misc/fixture/e/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -output misc/fixture/e/model_query.go misc/fixture/e; DO NOT EDIT 2 | 3 | package e 4 | 5 | import ( 6 | "github.com/favclip/qbg/qbgutils" 7 | "google.golang.org/appengine/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Foo *SampleQueryProperty 15 | } 16 | 17 | // SampleQueryProperty has property information for SampleQueryBuilder. 18 | type SampleQueryProperty struct { 19 | bldr *SampleQueryBuilder 20 | name string 21 | } 22 | 23 | // NewSampleQueryBuilder create new SampleQueryBuilder. 24 | func NewSampleQueryBuilder() *SampleQueryBuilder { 25 | return NewSampleQueryBuilderWithKind("Sample") 26 | } 27 | 28 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 29 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 30 | q := datastore.NewQuery(kind) 31 | bldr := &SampleQueryBuilder{q: q} 32 | bldr.Foo = &SampleQueryProperty{ 33 | bldr: bldr, 34 | name: "Foo", 35 | } 36 | 37 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 38 | bldr.plugin = plugger.Plugin() 39 | bldr.plugin.Init("Sample") 40 | } 41 | 42 | return bldr 43 | } 44 | 45 | // Ancestor sets parent key to ancestor query. 46 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 47 | bldr.q = bldr.q.Ancestor(parentKey) 48 | if bldr.plugin != nil { 49 | bldr.plugin.Ancestor(parentKey) 50 | } 51 | return bldr 52 | } 53 | 54 | // KeysOnly sets keys only option to query. 55 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 56 | bldr.q = bldr.q.KeysOnly() 57 | if bldr.plugin != nil { 58 | bldr.plugin.KeysOnly() 59 | } 60 | return bldr 61 | } 62 | 63 | // Start setup to query. 64 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 65 | bldr.q = bldr.q.Start(cur) 66 | if bldr.plugin != nil { 67 | bldr.plugin.Start(cur) 68 | } 69 | return bldr 70 | } 71 | 72 | // Offset setup to query. 73 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 74 | bldr.q = bldr.q.Offset(offset) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Offset(offset) 77 | } 78 | return bldr 79 | } 80 | 81 | // Limit setup to query. 82 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 83 | bldr.q = bldr.q.Limit(limit) 84 | if bldr.plugin != nil { 85 | bldr.plugin.Limit(limit) 86 | } 87 | return bldr 88 | } 89 | 90 | // Query returns *datastore.Query. 91 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 92 | return bldr.q 93 | } 94 | 95 | // Filter with op & value. 96 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 97 | switch op { 98 | case "<=": 99 | p.LessThanOrEqual(value) 100 | case ">=": 101 | p.GreaterThanOrEqual(value) 102 | case "<": 103 | p.LessThan(value) 104 | case ">": 105 | p.GreaterThan(value) 106 | case "=": 107 | p.Equal(value) 108 | default: 109 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 110 | } 111 | if p.bldr.plugin != nil { 112 | p.bldr.plugin.Filter(p.name, op, value) 113 | } 114 | return p.bldr 115 | } 116 | 117 | // LessThanOrEqual filter with value. 118 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 119 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 120 | if p.bldr.plugin != nil { 121 | p.bldr.plugin.Filter(p.name, "<=", value) 122 | } 123 | return p.bldr 124 | } 125 | 126 | // GreaterThanOrEqual filter with value. 127 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 128 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 129 | if p.bldr.plugin != nil { 130 | p.bldr.plugin.Filter(p.name, ">=", value) 131 | } 132 | return p.bldr 133 | } 134 | 135 | // LessThan filter with value. 136 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 137 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, "<", value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // GreaterThan filter with value. 145 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, ">", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // Equal filter with value. 154 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, "=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // Asc order. 163 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 164 | p.bldr.q = p.bldr.q.Order(p.name) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Asc(p.name) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // Desc order. 172 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 173 | p.bldr.q = p.bldr.q.Order("-" + p.name) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Desc(p.name) 176 | } 177 | return p.bldr 178 | } 179 | -------------------------------------------------------------------------------- /misc/fixture/e/model_test.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | import "testing" 4 | 5 | func TestPlugin(t *testing.T) { 6 | qb := NewSampleQueryBuilder() 7 | qb.Foo.Equal("test") 8 | qb.Foo.Desc() 9 | qb.KeysOnly() 10 | qb.Offset(0) 11 | qb.Limit(10) 12 | 13 | mp, ok := qb.Plugin().(*MemcacheQueryPlugin) 14 | if !ok { 15 | t.Fatal("Plugin is not MemcacheQueryPlugin") 16 | } 17 | 18 | if str := mp.QueryString(); str != `k=Sample:?Foo="test":D=Foo:!k:!o=0:!l=10` { 19 | t.Fatalf("unexpected: %s", str) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /misc/fixture/f/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go . 2 | package f 3 | 4 | // +qbg 5 | type Sample struct { 6 | Kind string `goon:"kind,sample_kind"` 7 | Foo string 8 | } 9 | -------------------------------------------------------------------------------- /misc/fixture/f/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -output misc/fixture/f/model_query.go misc/fixture/f; DO NOT EDIT 2 | 3 | package f 4 | 5 | import ( 6 | "github.com/favclip/qbg/qbgutils" 7 | "google.golang.org/appengine/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Kind *SampleQueryProperty 15 | Foo *SampleQueryProperty 16 | } 17 | 18 | // SampleQueryProperty has property information for SampleQueryBuilder. 19 | type SampleQueryProperty struct { 20 | bldr *SampleQueryBuilder 21 | name string 22 | } 23 | 24 | // NewSampleQueryBuilder create new SampleQueryBuilder. 25 | func NewSampleQueryBuilder() *SampleQueryBuilder { 26 | return NewSampleQueryBuilderWithKind("sample_kind") 27 | } 28 | 29 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 30 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 31 | q := datastore.NewQuery(kind) 32 | bldr := &SampleQueryBuilder{q: q} 33 | bldr.Kind = &SampleQueryProperty{ 34 | bldr: bldr, 35 | name: "Kind", 36 | } 37 | bldr.Foo = &SampleQueryProperty{ 38 | bldr: bldr, 39 | name: "Foo", 40 | } 41 | 42 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 43 | bldr.plugin = plugger.Plugin() 44 | bldr.plugin.Init("Sample") 45 | } 46 | 47 | return bldr 48 | } 49 | 50 | // Ancestor sets parent key to ancestor query. 51 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 52 | bldr.q = bldr.q.Ancestor(parentKey) 53 | if bldr.plugin != nil { 54 | bldr.plugin.Ancestor(parentKey) 55 | } 56 | return bldr 57 | } 58 | 59 | // KeysOnly sets keys only option to query. 60 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 61 | bldr.q = bldr.q.KeysOnly() 62 | if bldr.plugin != nil { 63 | bldr.plugin.KeysOnly() 64 | } 65 | return bldr 66 | } 67 | 68 | // Start setup to query. 69 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 70 | bldr.q = bldr.q.Start(cur) 71 | if bldr.plugin != nil { 72 | bldr.plugin.Start(cur) 73 | } 74 | return bldr 75 | } 76 | 77 | // Offset setup to query. 78 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 79 | bldr.q = bldr.q.Offset(offset) 80 | if bldr.plugin != nil { 81 | bldr.plugin.Offset(offset) 82 | } 83 | return bldr 84 | } 85 | 86 | // Limit setup to query. 87 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 88 | bldr.q = bldr.q.Limit(limit) 89 | if bldr.plugin != nil { 90 | bldr.plugin.Limit(limit) 91 | } 92 | return bldr 93 | } 94 | 95 | // Query returns *datastore.Query. 96 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 97 | return bldr.q 98 | } 99 | 100 | // Filter with op & value. 101 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 102 | switch op { 103 | case "<=": 104 | p.LessThanOrEqual(value) 105 | case ">=": 106 | p.GreaterThanOrEqual(value) 107 | case "<": 108 | p.LessThan(value) 109 | case ">": 110 | p.GreaterThan(value) 111 | case "=": 112 | p.Equal(value) 113 | default: 114 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 115 | } 116 | if p.bldr.plugin != nil { 117 | p.bldr.plugin.Filter(p.name, op, value) 118 | } 119 | return p.bldr 120 | } 121 | 122 | // LessThanOrEqual filter with value. 123 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 124 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 125 | if p.bldr.plugin != nil { 126 | p.bldr.plugin.Filter(p.name, "<=", value) 127 | } 128 | return p.bldr 129 | } 130 | 131 | // GreaterThanOrEqual filter with value. 132 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 133 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 134 | if p.bldr.plugin != nil { 135 | p.bldr.plugin.Filter(p.name, ">=", value) 136 | } 137 | return p.bldr 138 | } 139 | 140 | // LessThan filter with value. 141 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 142 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 143 | if p.bldr.plugin != nil { 144 | p.bldr.plugin.Filter(p.name, "<", value) 145 | } 146 | return p.bldr 147 | } 148 | 149 | // GreaterThan filter with value. 150 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 151 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 152 | if p.bldr.plugin != nil { 153 | p.bldr.plugin.Filter(p.name, ">", value) 154 | } 155 | return p.bldr 156 | } 157 | 158 | // Equal filter with value. 159 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 160 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 161 | if p.bldr.plugin != nil { 162 | p.bldr.plugin.Filter(p.name, "=", value) 163 | } 164 | return p.bldr 165 | } 166 | 167 | // Asc order. 168 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 169 | p.bldr.q = p.bldr.q.Order(p.name) 170 | if p.bldr.plugin != nil { 171 | p.bldr.plugin.Asc(p.name) 172 | } 173 | return p.bldr 174 | } 175 | 176 | // Desc order. 177 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 178 | p.bldr.q = p.bldr.q.Order("-" + p.name) 179 | if p.bldr.plugin != nil { 180 | p.bldr.plugin.Desc(p.name) 181 | } 182 | return p.bldr 183 | } 184 | -------------------------------------------------------------------------------- /misc/fixture/f/model_test.go: -------------------------------------------------------------------------------- 1 | package f 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mjibson/goon" 7 | "google.golang.org/appengine" 8 | "google.golang.org/appengine/aetest" 9 | ) 10 | 11 | func TestGoonKind(t *testing.T) { 12 | opts := &aetest.Options{ 13 | StronglyConsistentDatastore: true, 14 | } 15 | inst, err := aetest.NewInstance(opts) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | defer inst.Close() 20 | 21 | req, err := inst.NewRequest("GET", "/", nil) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | c := appengine.NewContext(req) 26 | 27 | g := goon.FromContext(c) 28 | 29 | entity1 := &Sample{ 30 | Foo: "foo1", 31 | } 32 | key1, err := g.Put(entity1) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | t.Logf("key1: %s", key1.String()) 37 | 38 | entity2 := &Sample{ 39 | Kind: "sample_specified", 40 | Foo: "foo1", 41 | } 42 | key2, err := g.Put(entity2) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | t.Logf("key2: %s", key2.String()) 47 | 48 | cnt1, err := NewSampleQueryBuilder().Query().Count(c) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | if cnt1 != 1 { 53 | t.Errorf("unexpected: %v", cnt1) 54 | } 55 | 56 | cnt2, err := NewSampleQueryBuilderWithKind(entity2.Kind).Query().Count(c) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | if cnt2 != 1 { 61 | t.Errorf("unexpected: %v", cnt2) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /misc/fixture/g/model.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | // +qbg 4 | type FooBar struct { 5 | Foo string 6 | } 7 | 8 | // +qbg 9 | type AEData struct { 10 | Foo string 11 | } 12 | 13 | // +qbg 14 | type IIS struct { 15 | Foo string 16 | } 17 | -------------------------------------------------------------------------------- /misc/fixture/g/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -output misc/fixture/g/model_query.go -private misc/fixture/g; DO NOT EDIT 2 | 3 | package g 4 | 5 | import ( 6 | "github.com/favclip/qbg/qbgutils" 7 | "google.golang.org/appengine/datastore" 8 | ) 9 | 10 | // fooBarQueryBuilder build query for fooBar. 11 | type fooBarQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Foo *fooBarQueryProperty 15 | } 16 | 17 | // fooBarQueryProperty has property information for fooBarQueryBuilder. 18 | type fooBarQueryProperty struct { 19 | bldr *fooBarQueryBuilder 20 | name string 21 | } 22 | 23 | // newFooBarQueryBuilder create new FooBarQueryBuilder. 24 | func newFooBarQueryBuilder() *fooBarQueryBuilder { 25 | return newFooBarQueryBuilderWithKind("fooBar") 26 | } 27 | 28 | // newFooBarQueryBuilderWithKind create new FooBarQueryBuilder with specific kind. 29 | func newFooBarQueryBuilderWithKind(kind string) *fooBarQueryBuilder { 30 | q := datastore.NewQuery(kind) 31 | bldr := &fooBarQueryBuilder{q: q} 32 | bldr.Foo = &fooBarQueryProperty{ 33 | bldr: bldr, 34 | name: "Foo", 35 | } 36 | 37 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 38 | bldr.plugin = plugger.Plugin() 39 | bldr.plugin.Init("FooBar") 40 | } 41 | 42 | return bldr 43 | } 44 | 45 | // Ancestor sets parent key to ancestor query. 46 | func (bldr *fooBarQueryBuilder) Ancestor(parentKey *datastore.Key) *fooBarQueryBuilder { 47 | bldr.q = bldr.q.Ancestor(parentKey) 48 | if bldr.plugin != nil { 49 | bldr.plugin.Ancestor(parentKey) 50 | } 51 | return bldr 52 | } 53 | 54 | // KeysOnly sets keys only option to query. 55 | func (bldr *fooBarQueryBuilder) KeysOnly() *fooBarQueryBuilder { 56 | bldr.q = bldr.q.KeysOnly() 57 | if bldr.plugin != nil { 58 | bldr.plugin.KeysOnly() 59 | } 60 | return bldr 61 | } 62 | 63 | // Start setup to query. 64 | func (bldr *fooBarQueryBuilder) Start(cur datastore.Cursor) *fooBarQueryBuilder { 65 | bldr.q = bldr.q.Start(cur) 66 | if bldr.plugin != nil { 67 | bldr.plugin.Start(cur) 68 | } 69 | return bldr 70 | } 71 | 72 | // Offset setup to query. 73 | func (bldr *fooBarQueryBuilder) Offset(offset int) *fooBarQueryBuilder { 74 | bldr.q = bldr.q.Offset(offset) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Offset(offset) 77 | } 78 | return bldr 79 | } 80 | 81 | // Limit setup to query. 82 | func (bldr *fooBarQueryBuilder) Limit(limit int) *fooBarQueryBuilder { 83 | bldr.q = bldr.q.Limit(limit) 84 | if bldr.plugin != nil { 85 | bldr.plugin.Limit(limit) 86 | } 87 | return bldr 88 | } 89 | 90 | // Query returns *datastore.Query. 91 | func (bldr *fooBarQueryBuilder) Query() *datastore.Query { 92 | return bldr.q 93 | } 94 | 95 | // Filter with op & value. 96 | func (p *fooBarQueryProperty) Filter(op string, value interface{}) *fooBarQueryBuilder { 97 | switch op { 98 | case "<=": 99 | p.LessThanOrEqual(value) 100 | case ">=": 101 | p.GreaterThanOrEqual(value) 102 | case "<": 103 | p.LessThan(value) 104 | case ">": 105 | p.GreaterThan(value) 106 | case "=": 107 | p.Equal(value) 108 | default: 109 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 110 | } 111 | if p.bldr.plugin != nil { 112 | p.bldr.plugin.Filter(p.name, op, value) 113 | } 114 | return p.bldr 115 | } 116 | 117 | // LessThanOrEqual filter with value. 118 | func (p *fooBarQueryProperty) LessThanOrEqual(value interface{}) *fooBarQueryBuilder { 119 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 120 | if p.bldr.plugin != nil { 121 | p.bldr.plugin.Filter(p.name, "<=", value) 122 | } 123 | return p.bldr 124 | } 125 | 126 | // GreaterThanOrEqual filter with value. 127 | func (p *fooBarQueryProperty) GreaterThanOrEqual(value interface{}) *fooBarQueryBuilder { 128 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 129 | if p.bldr.plugin != nil { 130 | p.bldr.plugin.Filter(p.name, ">=", value) 131 | } 132 | return p.bldr 133 | } 134 | 135 | // LessThan filter with value. 136 | func (p *fooBarQueryProperty) LessThan(value interface{}) *fooBarQueryBuilder { 137 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, "<", value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // GreaterThan filter with value. 145 | func (p *fooBarQueryProperty) GreaterThan(value interface{}) *fooBarQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, ">", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // Equal filter with value. 154 | func (p *fooBarQueryProperty) Equal(value interface{}) *fooBarQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, "=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // Asc order. 163 | func (p *fooBarQueryProperty) Asc() *fooBarQueryBuilder { 164 | p.bldr.q = p.bldr.q.Order(p.name) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Asc(p.name) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // Desc order. 172 | func (p *fooBarQueryProperty) Desc() *fooBarQueryBuilder { 173 | p.bldr.q = p.bldr.q.Order("-" + p.name) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Desc(p.name) 176 | } 177 | return p.bldr 178 | } 179 | 180 | // aeDataQueryBuilder build query for aeData. 181 | type aeDataQueryBuilder struct { 182 | q *datastore.Query 183 | plugin qbgutils.Plugin 184 | Foo *aeDataQueryProperty 185 | } 186 | 187 | // aeDataQueryProperty has property information for aeDataQueryBuilder. 188 | type aeDataQueryProperty struct { 189 | bldr *aeDataQueryBuilder 190 | name string 191 | } 192 | 193 | // newAEDataQueryBuilder create new AEDataQueryBuilder. 194 | func newAEDataQueryBuilder() *aeDataQueryBuilder { 195 | return newAEDataQueryBuilderWithKind("aeData") 196 | } 197 | 198 | // newAEDataQueryBuilderWithKind create new AEDataQueryBuilder with specific kind. 199 | func newAEDataQueryBuilderWithKind(kind string) *aeDataQueryBuilder { 200 | q := datastore.NewQuery(kind) 201 | bldr := &aeDataQueryBuilder{q: q} 202 | bldr.Foo = &aeDataQueryProperty{ 203 | bldr: bldr, 204 | name: "Foo", 205 | } 206 | 207 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 208 | bldr.plugin = plugger.Plugin() 209 | bldr.plugin.Init("AEData") 210 | } 211 | 212 | return bldr 213 | } 214 | 215 | // Ancestor sets parent key to ancestor query. 216 | func (bldr *aeDataQueryBuilder) Ancestor(parentKey *datastore.Key) *aeDataQueryBuilder { 217 | bldr.q = bldr.q.Ancestor(parentKey) 218 | if bldr.plugin != nil { 219 | bldr.plugin.Ancestor(parentKey) 220 | } 221 | return bldr 222 | } 223 | 224 | // KeysOnly sets keys only option to query. 225 | func (bldr *aeDataQueryBuilder) KeysOnly() *aeDataQueryBuilder { 226 | bldr.q = bldr.q.KeysOnly() 227 | if bldr.plugin != nil { 228 | bldr.plugin.KeysOnly() 229 | } 230 | return bldr 231 | } 232 | 233 | // Start setup to query. 234 | func (bldr *aeDataQueryBuilder) Start(cur datastore.Cursor) *aeDataQueryBuilder { 235 | bldr.q = bldr.q.Start(cur) 236 | if bldr.plugin != nil { 237 | bldr.plugin.Start(cur) 238 | } 239 | return bldr 240 | } 241 | 242 | // Offset setup to query. 243 | func (bldr *aeDataQueryBuilder) Offset(offset int) *aeDataQueryBuilder { 244 | bldr.q = bldr.q.Offset(offset) 245 | if bldr.plugin != nil { 246 | bldr.plugin.Offset(offset) 247 | } 248 | return bldr 249 | } 250 | 251 | // Limit setup to query. 252 | func (bldr *aeDataQueryBuilder) Limit(limit int) *aeDataQueryBuilder { 253 | bldr.q = bldr.q.Limit(limit) 254 | if bldr.plugin != nil { 255 | bldr.plugin.Limit(limit) 256 | } 257 | return bldr 258 | } 259 | 260 | // Query returns *datastore.Query. 261 | func (bldr *aeDataQueryBuilder) Query() *datastore.Query { 262 | return bldr.q 263 | } 264 | 265 | // Filter with op & value. 266 | func (p *aeDataQueryProperty) Filter(op string, value interface{}) *aeDataQueryBuilder { 267 | switch op { 268 | case "<=": 269 | p.LessThanOrEqual(value) 270 | case ">=": 271 | p.GreaterThanOrEqual(value) 272 | case "<": 273 | p.LessThan(value) 274 | case ">": 275 | p.GreaterThan(value) 276 | case "=": 277 | p.Equal(value) 278 | default: 279 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 280 | } 281 | if p.bldr.plugin != nil { 282 | p.bldr.plugin.Filter(p.name, op, value) 283 | } 284 | return p.bldr 285 | } 286 | 287 | // LessThanOrEqual filter with value. 288 | func (p *aeDataQueryProperty) LessThanOrEqual(value interface{}) *aeDataQueryBuilder { 289 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 290 | if p.bldr.plugin != nil { 291 | p.bldr.plugin.Filter(p.name, "<=", value) 292 | } 293 | return p.bldr 294 | } 295 | 296 | // GreaterThanOrEqual filter with value. 297 | func (p *aeDataQueryProperty) GreaterThanOrEqual(value interface{}) *aeDataQueryBuilder { 298 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 299 | if p.bldr.plugin != nil { 300 | p.bldr.plugin.Filter(p.name, ">=", value) 301 | } 302 | return p.bldr 303 | } 304 | 305 | // LessThan filter with value. 306 | func (p *aeDataQueryProperty) LessThan(value interface{}) *aeDataQueryBuilder { 307 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 308 | if p.bldr.plugin != nil { 309 | p.bldr.plugin.Filter(p.name, "<", value) 310 | } 311 | return p.bldr 312 | } 313 | 314 | // GreaterThan filter with value. 315 | func (p *aeDataQueryProperty) GreaterThan(value interface{}) *aeDataQueryBuilder { 316 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 317 | if p.bldr.plugin != nil { 318 | p.bldr.plugin.Filter(p.name, ">", value) 319 | } 320 | return p.bldr 321 | } 322 | 323 | // Equal filter with value. 324 | func (p *aeDataQueryProperty) Equal(value interface{}) *aeDataQueryBuilder { 325 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 326 | if p.bldr.plugin != nil { 327 | p.bldr.plugin.Filter(p.name, "=", value) 328 | } 329 | return p.bldr 330 | } 331 | 332 | // Asc order. 333 | func (p *aeDataQueryProperty) Asc() *aeDataQueryBuilder { 334 | p.bldr.q = p.bldr.q.Order(p.name) 335 | if p.bldr.plugin != nil { 336 | p.bldr.plugin.Asc(p.name) 337 | } 338 | return p.bldr 339 | } 340 | 341 | // Desc order. 342 | func (p *aeDataQueryProperty) Desc() *aeDataQueryBuilder { 343 | p.bldr.q = p.bldr.q.Order("-" + p.name) 344 | if p.bldr.plugin != nil { 345 | p.bldr.plugin.Desc(p.name) 346 | } 347 | return p.bldr 348 | } 349 | 350 | // iisQueryBuilder build query for iis. 351 | type iisQueryBuilder struct { 352 | q *datastore.Query 353 | plugin qbgutils.Plugin 354 | Foo *iisQueryProperty 355 | } 356 | 357 | // iisQueryProperty has property information for iisQueryBuilder. 358 | type iisQueryProperty struct { 359 | bldr *iisQueryBuilder 360 | name string 361 | } 362 | 363 | // newIISQueryBuilder create new IISQueryBuilder. 364 | func newIISQueryBuilder() *iisQueryBuilder { 365 | return newIISQueryBuilderWithKind("iis") 366 | } 367 | 368 | // newIISQueryBuilderWithKind create new IISQueryBuilder with specific kind. 369 | func newIISQueryBuilderWithKind(kind string) *iisQueryBuilder { 370 | q := datastore.NewQuery(kind) 371 | bldr := &iisQueryBuilder{q: q} 372 | bldr.Foo = &iisQueryProperty{ 373 | bldr: bldr, 374 | name: "Foo", 375 | } 376 | 377 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 378 | bldr.plugin = plugger.Plugin() 379 | bldr.plugin.Init("IIS") 380 | } 381 | 382 | return bldr 383 | } 384 | 385 | // Ancestor sets parent key to ancestor query. 386 | func (bldr *iisQueryBuilder) Ancestor(parentKey *datastore.Key) *iisQueryBuilder { 387 | bldr.q = bldr.q.Ancestor(parentKey) 388 | if bldr.plugin != nil { 389 | bldr.plugin.Ancestor(parentKey) 390 | } 391 | return bldr 392 | } 393 | 394 | // KeysOnly sets keys only option to query. 395 | func (bldr *iisQueryBuilder) KeysOnly() *iisQueryBuilder { 396 | bldr.q = bldr.q.KeysOnly() 397 | if bldr.plugin != nil { 398 | bldr.plugin.KeysOnly() 399 | } 400 | return bldr 401 | } 402 | 403 | // Start setup to query. 404 | func (bldr *iisQueryBuilder) Start(cur datastore.Cursor) *iisQueryBuilder { 405 | bldr.q = bldr.q.Start(cur) 406 | if bldr.plugin != nil { 407 | bldr.plugin.Start(cur) 408 | } 409 | return bldr 410 | } 411 | 412 | // Offset setup to query. 413 | func (bldr *iisQueryBuilder) Offset(offset int) *iisQueryBuilder { 414 | bldr.q = bldr.q.Offset(offset) 415 | if bldr.plugin != nil { 416 | bldr.plugin.Offset(offset) 417 | } 418 | return bldr 419 | } 420 | 421 | // Limit setup to query. 422 | func (bldr *iisQueryBuilder) Limit(limit int) *iisQueryBuilder { 423 | bldr.q = bldr.q.Limit(limit) 424 | if bldr.plugin != nil { 425 | bldr.plugin.Limit(limit) 426 | } 427 | return bldr 428 | } 429 | 430 | // Query returns *datastore.Query. 431 | func (bldr *iisQueryBuilder) Query() *datastore.Query { 432 | return bldr.q 433 | } 434 | 435 | // Filter with op & value. 436 | func (p *iisQueryProperty) Filter(op string, value interface{}) *iisQueryBuilder { 437 | switch op { 438 | case "<=": 439 | p.LessThanOrEqual(value) 440 | case ">=": 441 | p.GreaterThanOrEqual(value) 442 | case "<": 443 | p.LessThan(value) 444 | case ">": 445 | p.GreaterThan(value) 446 | case "=": 447 | p.Equal(value) 448 | default: 449 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 450 | } 451 | if p.bldr.plugin != nil { 452 | p.bldr.plugin.Filter(p.name, op, value) 453 | } 454 | return p.bldr 455 | } 456 | 457 | // LessThanOrEqual filter with value. 458 | func (p *iisQueryProperty) LessThanOrEqual(value interface{}) *iisQueryBuilder { 459 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 460 | if p.bldr.plugin != nil { 461 | p.bldr.plugin.Filter(p.name, "<=", value) 462 | } 463 | return p.bldr 464 | } 465 | 466 | // GreaterThanOrEqual filter with value. 467 | func (p *iisQueryProperty) GreaterThanOrEqual(value interface{}) *iisQueryBuilder { 468 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 469 | if p.bldr.plugin != nil { 470 | p.bldr.plugin.Filter(p.name, ">=", value) 471 | } 472 | return p.bldr 473 | } 474 | 475 | // LessThan filter with value. 476 | func (p *iisQueryProperty) LessThan(value interface{}) *iisQueryBuilder { 477 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 478 | if p.bldr.plugin != nil { 479 | p.bldr.plugin.Filter(p.name, "<", value) 480 | } 481 | return p.bldr 482 | } 483 | 484 | // GreaterThan filter with value. 485 | func (p *iisQueryProperty) GreaterThan(value interface{}) *iisQueryBuilder { 486 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 487 | if p.bldr.plugin != nil { 488 | p.bldr.plugin.Filter(p.name, ">", value) 489 | } 490 | return p.bldr 491 | } 492 | 493 | // Equal filter with value. 494 | func (p *iisQueryProperty) Equal(value interface{}) *iisQueryBuilder { 495 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 496 | if p.bldr.plugin != nil { 497 | p.bldr.plugin.Filter(p.name, "=", value) 498 | } 499 | return p.bldr 500 | } 501 | 502 | // Asc order. 503 | func (p *iisQueryProperty) Asc() *iisQueryBuilder { 504 | p.bldr.q = p.bldr.q.Order(p.name) 505 | if p.bldr.plugin != nil { 506 | p.bldr.plugin.Asc(p.name) 507 | } 508 | return p.bldr 509 | } 510 | 511 | // Desc order. 512 | func (p *iisQueryProperty) Desc() *iisQueryBuilder { 513 | p.bldr.q = p.bldr.q.Order("-" + p.name) 514 | if p.bldr.plugin != nil { 515 | p.bldr.plugin.Desc(p.name) 516 | } 517 | return p.bldr 518 | } 519 | -------------------------------------------------------------------------------- /misc/fixture/h/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go -inlineinterfaces . 2 | package h 3 | 4 | // +qbg 5 | type Sample struct { 6 | Kind string `goon:"kind,sample_kind"` 7 | Foo string 8 | } 9 | -------------------------------------------------------------------------------- /misc/fixture/h/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -output misc/fixture/h/model_query.go -inlineinterfaces misc/fixture/h; DO NOT EDIT 2 | 3 | package h 4 | 5 | import ( 6 | "google.golang.org/appengine/datastore" 7 | ) 8 | 9 | // Plugin supply hook point for query constructions. 10 | type Plugin interface { 11 | Init(typeName string) 12 | Ancestor(ancestor *datastore.Key) 13 | KeysOnly() 14 | Start(cur datastore.Cursor) 15 | Offset(offset int) 16 | Limit(limit int) 17 | Filter(name, op string, value interface{}) 18 | Asc(name string) 19 | Desc(name string) 20 | } 21 | 22 | // Plugger supply Plugin component. 23 | type Plugger interface { 24 | Plugin() Plugin 25 | } 26 | 27 | // SampleQueryBuilder build query for Sample. 28 | type SampleQueryBuilder struct { 29 | q *datastore.Query 30 | plugin Plugin 31 | Kind *SampleQueryProperty 32 | Foo *SampleQueryProperty 33 | } 34 | 35 | // SampleQueryProperty has property information for SampleQueryBuilder. 36 | type SampleQueryProperty struct { 37 | bldr *SampleQueryBuilder 38 | name string 39 | } 40 | 41 | // NewSampleQueryBuilder create new SampleQueryBuilder. 42 | func NewSampleQueryBuilder() *SampleQueryBuilder { 43 | return NewSampleQueryBuilderWithKind("sample_kind") 44 | } 45 | 46 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 47 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 48 | q := datastore.NewQuery(kind) 49 | bldr := &SampleQueryBuilder{q: q} 50 | bldr.Kind = &SampleQueryProperty{ 51 | bldr: bldr, 52 | name: "Kind", 53 | } 54 | bldr.Foo = &SampleQueryProperty{ 55 | bldr: bldr, 56 | name: "Foo", 57 | } 58 | 59 | if plugger, ok := interface{}(bldr).(Plugger); ok { 60 | bldr.plugin = plugger.Plugin() 61 | bldr.plugin.Init("Sample") 62 | } 63 | 64 | return bldr 65 | } 66 | 67 | // Ancestor sets parent key to ancestor query. 68 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 69 | bldr.q = bldr.q.Ancestor(parentKey) 70 | if bldr.plugin != nil { 71 | bldr.plugin.Ancestor(parentKey) 72 | } 73 | return bldr 74 | } 75 | 76 | // KeysOnly sets keys only option to query. 77 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 78 | bldr.q = bldr.q.KeysOnly() 79 | if bldr.plugin != nil { 80 | bldr.plugin.KeysOnly() 81 | } 82 | return bldr 83 | } 84 | 85 | // Start setup to query. 86 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 87 | bldr.q = bldr.q.Start(cur) 88 | if bldr.plugin != nil { 89 | bldr.plugin.Start(cur) 90 | } 91 | return bldr 92 | } 93 | 94 | // Offset setup to query. 95 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 96 | bldr.q = bldr.q.Offset(offset) 97 | if bldr.plugin != nil { 98 | bldr.plugin.Offset(offset) 99 | } 100 | return bldr 101 | } 102 | 103 | // Limit setup to query. 104 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 105 | bldr.q = bldr.q.Limit(limit) 106 | if bldr.plugin != nil { 107 | bldr.plugin.Limit(limit) 108 | } 109 | return bldr 110 | } 111 | 112 | // Query returns *datastore.Query. 113 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 114 | return bldr.q 115 | } 116 | 117 | // Filter with op & value. 118 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 119 | switch op { 120 | case "<=": 121 | p.LessThanOrEqual(value) 122 | case ">=": 123 | p.GreaterThanOrEqual(value) 124 | case "<": 125 | p.LessThan(value) 126 | case ">": 127 | p.GreaterThan(value) 128 | case "=": 129 | p.Equal(value) 130 | default: 131 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 132 | } 133 | if p.bldr.plugin != nil { 134 | p.bldr.plugin.Filter(p.name, op, value) 135 | } 136 | return p.bldr 137 | } 138 | 139 | // LessThanOrEqual filter with value. 140 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 141 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 142 | if p.bldr.plugin != nil { 143 | p.bldr.plugin.Filter(p.name, "<=", value) 144 | } 145 | return p.bldr 146 | } 147 | 148 | // GreaterThanOrEqual filter with value. 149 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 150 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 151 | if p.bldr.plugin != nil { 152 | p.bldr.plugin.Filter(p.name, ">=", value) 153 | } 154 | return p.bldr 155 | } 156 | 157 | // LessThan filter with value. 158 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 159 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 160 | if p.bldr.plugin != nil { 161 | p.bldr.plugin.Filter(p.name, "<", value) 162 | } 163 | return p.bldr 164 | } 165 | 166 | // GreaterThan filter with value. 167 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 168 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 169 | if p.bldr.plugin != nil { 170 | p.bldr.plugin.Filter(p.name, ">", value) 171 | } 172 | return p.bldr 173 | } 174 | 175 | // Equal filter with value. 176 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 177 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 178 | if p.bldr.plugin != nil { 179 | p.bldr.plugin.Filter(p.name, "=", value) 180 | } 181 | return p.bldr 182 | } 183 | 184 | // Asc order. 185 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 186 | p.bldr.q = p.bldr.q.Order(p.name) 187 | if p.bldr.plugin != nil { 188 | p.bldr.plugin.Asc(p.name) 189 | } 190 | return p.bldr 191 | } 192 | 193 | // Desc order. 194 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 195 | p.bldr.q = p.bldr.q.Order("-" + p.name) 196 | if p.bldr.plugin != nil { 197 | p.bldr.plugin.Desc(p.name) 198 | } 199 | return p.bldr 200 | } 201 | -------------------------------------------------------------------------------- /misc/fixture/i/model.go: -------------------------------------------------------------------------------- 1 | package i 2 | 3 | import ( 4 | "context" 5 | "go.mercari.io/datastore" 6 | ) 7 | 8 | var _ datastore.PropertyTranslator = UserID(0) 9 | 10 | type contextClient struct{} 11 | 12 | const kindUser = "User" 13 | 14 | type UserID int64 15 | 16 | // +qbg 17 | type User struct { 18 | ID UserID `datastore:"-" boom:"id" json:"id"` 19 | Name string `json:"name"` 20 | MentorID UserID `json:"mentorID"` 21 | } 22 | 23 | func (id UserID) ToPropertyValue(ctx context.Context) (interface{}, error) { 24 | client := ctx.Value(contextClient{}).(datastore.Client) 25 | key := client.IDKey(kindUser, int64(id), nil) 26 | return key, nil 27 | } 28 | 29 | func (id UserID) FromPropertyValue(ctx context.Context, p datastore.Property) (dst interface{}, err error) { 30 | key, ok := p.Value.(datastore.Key) 31 | if !ok { 32 | return nil, datastore.ErrInvalidEntityType 33 | } 34 | return UserID(key.ID()), nil 35 | } 36 | -------------------------------------------------------------------------------- /misc/fixture/i/model_query.go: -------------------------------------------------------------------------------- 1 | // generated by qbg -output misc/fixture/i/model_query.go -usedatastorewrapper misc/fixture/i; DO NOT EDIT 2 | 3 | package i 4 | 5 | import ( 6 | "go.mercari.io/datastore" 7 | ) 8 | 9 | // Plugin supply hook point for query constructions. 10 | type Plugin interface { 11 | Init(typeName string) 12 | Ancestor(ancestor datastore.Key) 13 | KeysOnly() 14 | Start(cur datastore.Cursor) 15 | Offset(offset int) 16 | Limit(limit int) 17 | Filter(name, op string, value interface{}) 18 | Asc(name string) 19 | Desc(name string) 20 | } 21 | 22 | // Plugger supply Plugin component. 23 | type Plugger interface { 24 | Plugin() Plugin 25 | } 26 | 27 | // UserQueryBuilder build query for User. 28 | type UserQueryBuilder struct { 29 | q datastore.Query 30 | plugin Plugin 31 | ID *UserQueryProperty 32 | Name *UserQueryProperty 33 | MentorID *UserQueryProperty 34 | } 35 | 36 | // UserQueryProperty has property information for UserQueryBuilder. 37 | type UserQueryProperty struct { 38 | bldr *UserQueryBuilder 39 | name string 40 | } 41 | 42 | // NewUserQueryBuilder create new UserQueryBuilder. 43 | func NewUserQueryBuilder(client datastore.Client) *UserQueryBuilder { 44 | return NewUserQueryBuilderWithKind(client, "User") 45 | } 46 | 47 | // NewUserQueryBuilderWithKind create new UserQueryBuilder with specific kind. 48 | func NewUserQueryBuilderWithKind(client datastore.Client, kind string) *UserQueryBuilder { 49 | q := client.NewQuery(kind) 50 | bldr := &UserQueryBuilder{q: q} 51 | bldr.ID = &UserQueryProperty{ 52 | bldr: bldr, 53 | name: "__key__", 54 | } 55 | bldr.Name = &UserQueryProperty{ 56 | bldr: bldr, 57 | name: "Name", 58 | } 59 | bldr.MentorID = &UserQueryProperty{ 60 | bldr: bldr, 61 | name: "MentorID", 62 | } 63 | 64 | if plugger, ok := interface{}(bldr).(Plugger); ok { 65 | bldr.plugin = plugger.Plugin() 66 | bldr.plugin.Init("User") 67 | } 68 | 69 | return bldr 70 | } 71 | 72 | // Ancestor sets parent key to ancestor query. 73 | func (bldr *UserQueryBuilder) Ancestor(parentKey datastore.Key) *UserQueryBuilder { 74 | bldr.q = bldr.q.Ancestor(parentKey) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Ancestor(parentKey) 77 | } 78 | return bldr 79 | } 80 | 81 | // KeysOnly sets keys only option to query. 82 | func (bldr *UserQueryBuilder) KeysOnly() *UserQueryBuilder { 83 | bldr.q = bldr.q.KeysOnly() 84 | if bldr.plugin != nil { 85 | bldr.plugin.KeysOnly() 86 | } 87 | return bldr 88 | } 89 | 90 | // Start setup to query. 91 | func (bldr *UserQueryBuilder) Start(cur datastore.Cursor) *UserQueryBuilder { 92 | bldr.q = bldr.q.Start(cur) 93 | if bldr.plugin != nil { 94 | bldr.plugin.Start(cur) 95 | } 96 | return bldr 97 | } 98 | 99 | // Offset setup to query. 100 | func (bldr *UserQueryBuilder) Offset(offset int) *UserQueryBuilder { 101 | bldr.q = bldr.q.Offset(offset) 102 | if bldr.plugin != nil { 103 | bldr.plugin.Offset(offset) 104 | } 105 | return bldr 106 | } 107 | 108 | // Limit setup to query. 109 | func (bldr *UserQueryBuilder) Limit(limit int) *UserQueryBuilder { 110 | bldr.q = bldr.q.Limit(limit) 111 | if bldr.plugin != nil { 112 | bldr.plugin.Limit(limit) 113 | } 114 | return bldr 115 | } 116 | 117 | // Query returns *datastore.Query. 118 | func (bldr *UserQueryBuilder) Query() datastore.Query { 119 | return bldr.q 120 | } 121 | 122 | // Filter with op & value. 123 | func (p *UserQueryProperty) Filter(op string, value interface{}) *UserQueryBuilder { 124 | switch op { 125 | case "<=": 126 | p.LessThanOrEqual(value) 127 | case ">=": 128 | p.GreaterThanOrEqual(value) 129 | case "<": 130 | p.LessThan(value) 131 | case ">": 132 | p.GreaterThan(value) 133 | case "=": 134 | p.Equal(value) 135 | default: 136 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 137 | } 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, op, value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // LessThanOrEqual filter with value. 145 | func (p *UserQueryProperty) LessThanOrEqual(value interface{}) *UserQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, "<=", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // GreaterThanOrEqual filter with value. 154 | func (p *UserQueryProperty) GreaterThanOrEqual(value interface{}) *UserQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, ">=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // LessThan filter with value. 163 | func (p *UserQueryProperty) LessThan(value interface{}) *UserQueryBuilder { 164 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Filter(p.name, "<", value) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // GreaterThan filter with value. 172 | func (p *UserQueryProperty) GreaterThan(value interface{}) *UserQueryBuilder { 173 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Filter(p.name, ">", value) 176 | } 177 | return p.bldr 178 | } 179 | 180 | // Equal filter with value. 181 | func (p *UserQueryProperty) Equal(value interface{}) *UserQueryBuilder { 182 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 183 | if p.bldr.plugin != nil { 184 | p.bldr.plugin.Filter(p.name, "=", value) 185 | } 186 | return p.bldr 187 | } 188 | 189 | // Asc order. 190 | func (p *UserQueryProperty) Asc() *UserQueryBuilder { 191 | p.bldr.q = p.bldr.q.Order(p.name) 192 | if p.bldr.plugin != nil { 193 | p.bldr.plugin.Asc(p.name) 194 | } 195 | return p.bldr 196 | } 197 | 198 | // Desc order. 199 | func (p *UserQueryProperty) Desc() *UserQueryBuilder { 200 | p.bldr.q = p.bldr.q.Order("-" + p.name) 201 | if p.bldr.plugin != nil { 202 | p.bldr.plugin.Desc(p.name) 203 | } 204 | return p.bldr 205 | } 206 | -------------------------------------------------------------------------------- /qbgutils/utils.go: -------------------------------------------------------------------------------- 1 | package qbgutils 2 | 3 | import "google.golang.org/appengine/datastore" 4 | 5 | // Plugin supply hook point for query constructions. 6 | type Plugin interface { 7 | Init(typeName string) 8 | Ancestor(ancestor *datastore.Key) 9 | KeysOnly() 10 | Start(cur datastore.Cursor) 11 | Offset(offset int) 12 | Limit(limit int) 13 | Filter(name, op string, value interface{}) 14 | Asc(name string) 15 | Desc(name string) 16 | } 17 | 18 | // Plugger supply Plugin component. 19 | type Plugger interface { 20 | Plugin() Plugin 21 | } 22 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | dep ensure 4 | 5 | go fmt ./... 6 | go run cmd/qbg/main.go -type Sample -output misc/fixture/a/model_query.go misc/fixture/a 7 | go run cmd/qbg/main.go -type Sample -output misc/fixture/b/model_query.go misc/fixture/b 8 | go run cmd/qbg/main.go -output misc/fixture/c/model_query.go misc/fixture/c 9 | go run cmd/qbg/main.go -output misc/fixture/d/model_query.go misc/fixture/d 10 | go run cmd/qbg/main.go -output misc/fixture/e/model_query.go misc/fixture/e 11 | go run cmd/qbg/main.go -output misc/fixture/f/model_query.go misc/fixture/f 12 | go run cmd/qbg/main.go -output misc/fixture/g/model_query.go -private misc/fixture/g 13 | go run cmd/qbg/main.go -output misc/fixture/h/model_query.go -inlineinterfaces misc/fixture/h 14 | go run cmd/qbg/main.go -output misc/fixture/i/model_query.go -usedatastorewrapper misc/fixture/i 15 | -------------------------------------------------------------------------------- /template.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | const pluginTemplate = ` 4 | // Plugin supply hook point for query constructions. 5 | type Plugin interface { 6 | Init(typeName string) 7 | Ancestor(ancestor {{.DSKeyType}}) 8 | KeysOnly() 9 | Start(cur datastore.Cursor) 10 | Offset(offset int) 11 | Limit(limit int) 12 | Filter(name, op string, value interface{}) 13 | Asc(name string) 14 | Desc(name string) 15 | } 16 | 17 | // Plugger supply Plugin component. 18 | type Plugger interface { 19 | Plugin() Plugin 20 | } 21 | ` 22 | 23 | const structTemplate = ` 24 | {{$st := .}} 25 | // {{.Name}}QueryBuilder build query for {{.Name}}. 26 | type {{.Name}}QueryBuilder struct { 27 | q {{.DSQueryType}} 28 | plugin {{.PluginType}} 29 | {{- range $idx, $f := .Fields}} 30 | {{$f.Tag.PropertyNameAlter}} *{{$st.Name}}QueryProperty 31 | {{- end}} 32 | } 33 | 34 | // {{.Name}}QueryProperty has property information for {{.Name}}QueryBuilder. 35 | type {{.Name}}QueryProperty struct { 36 | bldr *{{.Name}}QueryBuilder 37 | name string 38 | } 39 | 40 | // {{.NewWord}}{{.SimpleName}}QueryBuilder create new {{.SimpleName}}QueryBuilder. 41 | func {{.NewWord}}{{.SimpleName}}QueryBuilder({{if .UserDatastoreWrapper}}client datastore.Client,{{end}}) *{{.Name}}QueryBuilder { 42 | return {{.NewWord}}{{.SimpleName}}QueryBuilderWithKind({{if .UserDatastoreWrapper}}client,{{end}}"{{.Kind}}") 43 | } 44 | 45 | // {{.NewWord}}{{.SimpleName}}QueryBuilderWithKind create new {{.SimpleName}}QueryBuilder with specific kind. 46 | func {{.NewWord}}{{.SimpleName}}QueryBuilderWithKind({{if .UserDatastoreWrapper}}client datastore.Client,{{end}}kind string) *{{.Name}}QueryBuilder { 47 | q := {{.DSNewQuery}}(kind) 48 | bldr := &{{.Name}}QueryBuilder{q:q} 49 | {{- range $idx, $f := .Fields}} 50 | bldr.{{$f.Tag.PropertyNameAlter}}= &{{$st.Name}}QueryProperty{ 51 | bldr : bldr, 52 | name : {{if $f.Tag.ID}} "__key__" {{else}} "{{$f.Tag.Name}}" {{end}}, 53 | } 54 | {{- end}} 55 | 56 | if plugger, ok := interface{}(bldr).({{.PluggerType}}); ok { 57 | bldr.plugin = plugger.Plugin() 58 | bldr.plugin.Init("{{.SimpleName}}") 59 | } 60 | 61 | return bldr 62 | } 63 | 64 | // Ancestor sets parent key to ancestor query. 65 | func (bldr *{{.Name}}QueryBuilder) Ancestor(parentKey {{.DSKeyType}}) *{{.Name}}QueryBuilder { 66 | bldr.q = bldr.q.Ancestor(parentKey) 67 | if bldr.plugin != nil { 68 | bldr.plugin.Ancestor(parentKey) 69 | } 70 | return bldr 71 | } 72 | 73 | // KeysOnly sets keys only option to query. 74 | func (bldr *{{.Name}}QueryBuilder) KeysOnly() *{{.Name}}QueryBuilder { 75 | bldr.q = bldr.q.KeysOnly() 76 | if bldr.plugin != nil { 77 | bldr.plugin.KeysOnly() 78 | } 79 | return bldr 80 | } 81 | 82 | // Start setup to query. 83 | func (bldr *{{.Name}}QueryBuilder) Start(cur datastore.Cursor) *{{.Name}}QueryBuilder { 84 | bldr.q = bldr.q.Start(cur) 85 | if bldr.plugin != nil { 86 | bldr.plugin.Start(cur) 87 | } 88 | return bldr 89 | } 90 | 91 | // Offset setup to query. 92 | func (bldr *{{.Name}}QueryBuilder) Offset(offset int) *{{.Name}}QueryBuilder { 93 | bldr.q = bldr.q.Offset(offset) 94 | if bldr.plugin != nil { 95 | bldr.plugin.Offset(offset) 96 | } 97 | return bldr 98 | } 99 | 100 | // Limit setup to query. 101 | func (bldr *{{.Name}}QueryBuilder) Limit(limit int) *{{.Name}}QueryBuilder { 102 | bldr.q = bldr.q.Limit(limit) 103 | if bldr.plugin != nil { 104 | bldr.plugin.Limit(limit) 105 | } 106 | return bldr 107 | } 108 | 109 | // Query returns *datastore.Query. 110 | func (bldr *{{.Name}}QueryBuilder) Query() {{.DSQueryType}} { 111 | return bldr.q 112 | } 113 | 114 | // Filter with op & value. 115 | func (p *{{.Name}}QueryProperty) Filter(op string, value interface{}) *{{.Name}}QueryBuilder { 116 | switch op { 117 | case "<=": 118 | p.LessThanOrEqual(value) 119 | case ">=": 120 | p.GreaterThanOrEqual(value) 121 | case "<": 122 | p.LessThan(value) 123 | case ">": 124 | p.GreaterThan(value) 125 | case "=": 126 | p.Equal(value) 127 | default: 128 | p.bldr.q = p.bldr.q.Filter(p.name + " " + op, value) // error raised by native query 129 | } 130 | if p.bldr.plugin != nil { 131 | p.bldr.plugin.Filter(p.name, op, value) 132 | } 133 | return p.bldr 134 | } 135 | 136 | // LessThanOrEqual filter with value. 137 | func (p *{{.Name}}QueryProperty) LessThanOrEqual(value interface{}) *{{.Name}}QueryBuilder { 138 | p.bldr.q = p.bldr.q.Filter(p.name + " <=", value) 139 | if p.bldr.plugin != nil { 140 | p.bldr.plugin.Filter(p.name,"<=", value) 141 | } 142 | return p.bldr 143 | } 144 | 145 | // GreaterThanOrEqual filter with value. 146 | func (p *{{.Name}}QueryProperty) GreaterThanOrEqual(value interface{}) *{{.Name}}QueryBuilder { 147 | p.bldr.q = p.bldr.q.Filter(p.name + " >=", value) 148 | if p.bldr.plugin != nil { 149 | p.bldr.plugin.Filter(p.name,">=", value) 150 | } 151 | return p.bldr 152 | } 153 | 154 | // LessThan filter with value. 155 | func (p *{{.Name}}QueryProperty) LessThan(value interface{}) *{{.Name}}QueryBuilder { 156 | p.bldr.q = p.bldr.q.Filter(p.name + " <", value) 157 | if p.bldr.plugin != nil { 158 | p.bldr.plugin.Filter(p.name,"<", value) 159 | } 160 | return p.bldr 161 | } 162 | 163 | // GreaterThan filter with value. 164 | func (p *{{.Name}}QueryProperty) GreaterThan(value interface{}) *{{.Name}}QueryBuilder { 165 | p.bldr.q = p.bldr.q.Filter(p.name + " >", value) 166 | if p.bldr.plugin != nil { 167 | p.bldr.plugin.Filter(p.name,">", value) 168 | } 169 | return p.bldr 170 | } 171 | 172 | // Equal filter with value. 173 | func (p *{{.Name}}QueryProperty) Equal(value interface{}) *{{.Name}}QueryBuilder { 174 | p.bldr.q = p.bldr.q.Filter(p.name + " =", value) 175 | if p.bldr.plugin != nil { 176 | p.bldr.plugin.Filter(p.name,"=", value) 177 | } 178 | return p.bldr 179 | } 180 | 181 | // Asc order. 182 | func (p *{{.Name}}QueryProperty) Asc() *{{.Name}}QueryBuilder { 183 | p.bldr.q = p.bldr.q.Order(p.name) 184 | if p.bldr.plugin != nil { 185 | p.bldr.plugin.Asc(p.name) 186 | } 187 | return p.bldr 188 | } 189 | 190 | // Desc order. 191 | func (p *{{.Name}}QueryProperty) Desc() *{{.Name}}QueryBuilder { 192 | p.bldr.q = p.bldr.q.Order("-" + p.name) 193 | if p.bldr.plugin != nil { 194 | p.bldr.plugin.Desc(p.name) 195 | } 196 | return p.bldr 197 | } 198 | 199 | ` 200 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | packages=$(go list ./...) 4 | 5 | goapp test $packages 6 | -------------------------------------------------------------------------------- /usage_test.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/favclip/qbg/misc/fixture/a" 7 | "github.com/favclip/qbg/misc/fixture/e" 8 | "google.golang.org/appengine/aetest" 9 | "google.golang.org/appengine/datastore" 10 | "google.golang.org/appengine/memcache" 11 | ) 12 | 13 | func TestBasicUsage1(t *testing.T) { 14 | c, closer, err := aetest.NewContext() 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | defer closer() 19 | 20 | src := &a.Sample{"Foo!"} 21 | key := datastore.NewIncompleteKey(c, "Sample", nil) 22 | _, err = datastore.Put(c, key, src) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | builder := a.NewSampleQueryBuilder() 28 | builder.Foo.GreaterThan("Foo") 29 | iter := builder.Query().Run(c) 30 | for { 31 | src := &a.Sample{} 32 | key, err = iter.Next(src) 33 | if err == datastore.Done { 34 | break 35 | } else if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | t.Logf("key: %#v, entity: %#v", key, src) 40 | } 41 | } 42 | 43 | func TestBasicUsage2(t *testing.T) { 44 | c, closer, err := aetest.NewContext() 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | defer closer() 49 | 50 | src := &a.Sample{"Foo!"} 51 | key := datastore.NewIncompleteKey(c, "Sample", nil) 52 | _, err = datastore.Put(c, key, src) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | 57 | builder := a.NewSampleQueryBuilder() 58 | builder.Foo.GreaterThan("Foo").KeysOnly().Limit(3) 59 | iter := builder.Query().Run(c) 60 | for { 61 | key, err = iter.Next(nil) 62 | if err == datastore.Done { 63 | break 64 | } else if err != nil { 65 | t.Fatal(err) 66 | } 67 | src := &a.Sample{} 68 | err = datastore.Get(c, key, src) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | 73 | t.Logf("key: %#v, entity: %#v", key, src) 74 | } 75 | } 76 | 77 | func TestPluginUsage(t *testing.T) { 78 | c, closer, err := aetest.NewContext() 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | defer closer() 83 | 84 | parentKey := datastore.NewKey(c, "Test", "T", 0, nil) 85 | 86 | { 87 | // put for Datastore 88 | src := &e.Sample{"Foo!"} 89 | key := datastore.NewIncompleteKey(c, "Sample", parentKey) 90 | _, err = datastore.Put(c, key, src) 91 | if err != nil { 92 | t.Fatal(err) 93 | } 94 | _, err = memcache.Increment(c, "counter", 1, 0) // shift memcache counter 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | } 99 | 100 | builder := e.NewSampleQueryBuilder() 101 | mp, ok := builder.Plugin().(*e.MemcacheQueryPlugin) 102 | if !ok { 103 | t.Fatal("Plugin is not MemcacheQueryPlugin") 104 | } 105 | memcacheCounter, err := memcache.Increment(c, "counter", 0, 0) 106 | if err != nil { 107 | t.Fatal(err) 108 | } 109 | mp.AddCounter(memcacheCounter) 110 | 111 | builder.Ancestor(parentKey).Foo.GreaterThan("Fo").Limit(3) 112 | if str := mp.QueryString(); str != `k=Sample:1:!a=/Test,T:?Foo>"Fo":!l=3` { 113 | t.Fatalf("unexpected: %s", str) 114 | } 115 | 116 | { 117 | // get from Memcache 118 | var list []*e.Sample 119 | _, err = memcache.Gob.Get(c, mp.QueryString(), &list) 120 | if err == memcache.ErrCacheMiss { 121 | // continue 122 | } else if err != nil { 123 | t.Fatal(err) 124 | } else { 125 | t.Log(list) 126 | return 127 | } 128 | } 129 | 130 | { 131 | // get from Datastore 132 | iter := builder.Query().Run(c) 133 | var list []*e.Sample 134 | for { 135 | src := &e.Sample{} 136 | _, err = iter.Next(src) 137 | if err == datastore.Done { 138 | break 139 | } else if err != nil { 140 | t.Fatal(err) 141 | } 142 | list = append(list, src) 143 | } 144 | 145 | // put for Memcache 146 | err = memcache.Gob.Set(c, &memcache.Item{ 147 | Key: mp.QueryString(), 148 | Object: list, 149 | }) 150 | if err != nil { 151 | t.Fatal(err) 152 | } 153 | if len(list) != 1 { 154 | t.Fatalf("unexpected %d", len(list)) 155 | } 156 | } 157 | 158 | { 159 | // get from Memcache 160 | var list []*e.Sample 161 | _, err = memcache.Gob.Get(c, mp.QueryString(), &list) 162 | if err == memcache.ErrCacheMiss { 163 | t.Fatalf("query result is not in memcache") 164 | } else if err != nil { 165 | t.Fatal(err) 166 | } else { 167 | t.Log(list) 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /v2/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .idea/ 4 | *.iml 5 | 6 | vendor/ 7 | -------------------------------------------------------------------------------- /v2/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 tv-asahi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /v2/README.md: -------------------------------------------------------------------------------- 1 | # qbg 2 | 3 | `Query Builder Generator`. 4 | 5 | ## Description 6 | 7 | `qbg` can generate type safe wrapper for appengine datastore. 8 | 9 | If you use string literal for building query, You shoots your foot when you do typo. 10 | qbg generate appengine datastore wrapper code. It is type safe. Your mistake will be found by go compiler. 11 | 12 | ``` 13 | type User struct { 14 | Name string 15 | } 16 | ``` 17 | 18 | ``` 19 | user := &User { 20 | "go-chan", 21 | } 22 | builder := NewUserQueryBuilder() 23 | builder.Name.Equal("go-chan") 24 | cnt, err := builder.Query().Count(c) 25 | ``` 26 | 27 | ### Example 28 | 29 | [from](https://github.com/favclip/qbg/blob/master/misc/fixture/a/model.go): 30 | 31 | ``` 32 | type Sample struct { 33 | Foo string 34 | } 35 | ``` 36 | 37 | [to](https://github.com/favclip/qbg/blob/master/misc/fixture/a/model_query.go): 38 | 39 | ``` 40 | // generated! 41 | // for Sample 42 | type SampleQueryBuilder struct { 43 | q *datastore.Query 44 | plugin qbgutils.Plugin 45 | Foo *SampleQueryProperty 46 | } 47 | 48 | type SampleQueryProperty struct { 49 | bldr *SampleQueryBuilder 50 | name string 51 | } 52 | ``` 53 | 54 | usage: 55 | 56 | ``` 57 | src := &Sample{"Foo!"} 58 | 59 | builder := NewSampleQueryBuilder() // generated! 60 | builder.Foo.GreaterThanOrEqual("Foo") 61 | cnt, err := builder.Query().Count(c) 62 | ``` 63 | 64 | [other example](https://github.com/favclip/qbg/blob/master/usage_test.go). 65 | 66 | ### With `go generate` 67 | 68 | ``` 69 | $ ls -la . 70 | total 8 71 | drwxr-xr-x@ 3 vvakame staff 102 10 13 17:39 . 72 | drwxr-xr-x@ 7 vvakame staff 238 8 14 18:26 .. 73 | -rw-r--r--@ 1 vvakame staff 178 8 14 18:26 model.go 74 | $ cat model.go 75 | //go:generate qbg -output model_query.go . 76 | 77 | package c 78 | 79 | import "time" 80 | 81 | // +qbg 82 | type Sample struct { 83 | ID int64 `goon:"id"` 84 | CreatedAt time.Time `datastore:",noindex"` 85 | } 86 | $ go generate 87 | $ ls -la . 88 | total 16 89 | drwxr-xr-x@ 4 vvakame staff 136 10 13 17:40 . 90 | drwxr-xr-x@ 7 vvakame staff 238 8 14 18:26 .. 91 | -rw-r--r--@ 1 vvakame staff 178 8 14 18:26 model.go 92 | -rw-r--r-- 1 vvakame staff 3709 10 13 17:40 model_query.go 93 | ``` 94 | 95 | ### Recommend 96 | 97 | Please use with [goon](https://github.com/mjibson/goon). 98 | 99 | ## Installation 100 | 101 | ``` 102 | $ go get -u github.com/favclip/qbg/v2/cmd/qbg 103 | $ qbg 104 | Usage of qbg: 105 | qbg [flags] [directory] 106 | qbg [flags] files... # Must be a single package 107 | Flags: 108 | -output="": output file name; default srcdir/_query.go 109 | -type="": comma-separated list of type names; must be set 110 | ``` 111 | 112 | ## Command sample 113 | 114 | Model with type specific option. 115 | 116 | ``` 117 | $ cat misc/fixture/a/model.go 118 | package a 119 | 120 | // test for basic struct definition 121 | 122 | type Sample struct { 123 | Foo string 124 | } 125 | $ qbg -type Sample -output misc/fixture/a/model_query.go misc/fixture/a 126 | ``` 127 | 128 | Model with tagged comment. 129 | 130 | ``` 131 | $ cat misc/fixture/c/model.go 132 | //go:generate qbg -output model_query.go . 133 | 134 | package c 135 | 136 | import "time" 137 | 138 | // +qbg 139 | type Sample struct { 140 | ID int64 `goon:"id"` 141 | CreatedAt time.Time `datastore:",noindex"` 142 | } 143 | $ qbg -output misc/fixture/d/model_query.go misc/fixture/d 144 | ``` 145 | -------------------------------------------------------------------------------- /v2/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: go 2 | api_version: go1.16 3 | 4 | handlers: 5 | - url: /.* 6 | script: _go_app 7 | -------------------------------------------------------------------------------- /v2/cmd/qbg/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/favclip/genbase" 13 | "github.com/favclip/qbg/v2" 14 | ) 15 | 16 | var ( 17 | typeNames = flag.String("type", "", "comma-separated list of type names; must be set") 18 | output = flag.String("output", "", "output file name; default srcdir/_query.go") 19 | private = flag.Bool("private", false, "generated type name; export or unexport") 20 | inlineInterfaces = flag.Bool("inlineinterfaces", false, "generate interfaces to inline; don't use qbgutils") 21 | useDatastoreWrapper = flag.Bool("usedatastorewrapper", false, "use go.mercari.io/datastore/v2") 22 | ) 23 | 24 | // Usage is a replacement usage function for the flags package. 25 | func Usage() { 26 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 27 | fmt.Fprint(os.Stderr, "\tqbg [flags] [directory]\n") 28 | fmt.Fprint(os.Stderr, "\tqbg [flags] files... # Must be a single package\n") 29 | fmt.Fprint(os.Stderr, "Flags:\n") 30 | flag.PrintDefaults() 31 | os.Exit(2) 32 | } 33 | 34 | func main() { 35 | log.SetFlags(0) 36 | log.SetPrefix("qbg: ") 37 | flag.Usage = Usage 38 | flag.Parse() 39 | 40 | // We accept either one directory or a list of files. Which do we have? 41 | args := flag.Args() 42 | if len(args) == 0 { 43 | // Default: process whole package in current directory. 44 | args = []string{"."} 45 | } 46 | 47 | var dir string 48 | var pInfo *genbase.PackageInfo 49 | var err error 50 | p := &genbase.Parser{SkipSemanticsCheck: true} 51 | if len(args) == 1 && isDirectory(args[0]) { 52 | dir = args[0] 53 | pInfo, err = p.ParsePackageDir(dir) 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | } else { 58 | dir = filepath.Dir(args[0]) 59 | pInfo, err = p.ParsePackageFiles(args) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | } 64 | 65 | var typeInfos genbase.TypeInfos 66 | if len(*typeNames) == 0 { 67 | typeInfos = pInfo.CollectTaggedTypeInfos("+qbg") 68 | } else { 69 | typeInfos = pInfo.CollectTypeInfos(strings.Split(*typeNames, ",")) 70 | } 71 | 72 | if len(typeInfos) == 0 { 73 | flag.Usage() 74 | } 75 | 76 | bu := qbg.BuildSource{} 77 | bu.InlineInterfaces = *inlineInterfaces 78 | bu.UseDatastoreWrapper = *useDatastoreWrapper 79 | if bu.UseDatastoreWrapper { 80 | // go.mercari.io/datastore/v2 can't use qbgutils 81 | bu.InlineInterfaces = true 82 | } 83 | err = bu.Parse(pInfo, typeInfos) 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | for _, st := range bu.Structs { 88 | st.Private = *private 89 | } 90 | 91 | // Format the output. 92 | src, err := bu.Emit(nil) 93 | if err != nil { 94 | log.Printf("warning: internal error: invalid Go generated: %s", err) 95 | log.Print("warning: compile the package to analyze the error") 96 | } 97 | 98 | // Write to file. 99 | outputName := *output 100 | if outputName == "" { 101 | baseName := fmt.Sprintf("%s_query.go", typeInfos[0].Name()) 102 | outputName = filepath.Join(dir, strings.ToLower(baseName)) 103 | } 104 | err = ioutil.WriteFile(outputName, src, 0644) 105 | if err != nil { 106 | log.Fatalf("writing output: %s", err) 107 | } 108 | } 109 | 110 | // isDirectory reports whether the named file is a directory. 111 | func isDirectory(name string) bool { 112 | info, err := os.Stat(name) 113 | if err != nil { 114 | log.Fatal(err) 115 | } 116 | return info.IsDir() 117 | } 118 | -------------------------------------------------------------------------------- /v2/generator.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "strings" 7 | "text/template" 8 | "unicode" 9 | 10 | "github.com/favclip/genbase" 11 | ) 12 | 13 | // BuildSource represents source code of assembling.. 14 | type BuildSource struct { 15 | g *genbase.Generator 16 | pkg *genbase.PackageInfo 17 | typeInfos genbase.TypeInfos 18 | 19 | InlineInterfaces bool 20 | UseDatastoreWrapper bool 21 | Structs []*BuildStruct 22 | } 23 | 24 | // BuildStruct represents struct of assembling.. 25 | type BuildStruct struct { 26 | parent *BuildSource 27 | typeInfo *genbase.TypeInfo 28 | 29 | Private bool 30 | Fields []*BuildField 31 | } 32 | 33 | // BuildField represents field of BuildStruct. 34 | type BuildField struct { 35 | parent *BuildStruct 36 | fieldInfo *genbase.FieldInfo 37 | 38 | Name string 39 | Tag *BuildTag 40 | } 41 | 42 | // BuildTag represents tag of BuildField. 43 | type BuildTag struct { 44 | field *BuildField 45 | 46 | Kind string // e.g. `goon:"kind,FooKind"` 47 | Name string 48 | PropertyNameAlter string // e.g. `qbg:"StartAt"` 49 | ID bool 50 | Ignore bool // e.g. Secret string `datastore:"-"` 51 | NoIndex bool // e.g. no index `datastore:",noindex"` 52 | } 53 | 54 | // Parse construct *BuildSource from package & type information. 55 | // deprecated. use *BuildSource#Parse instead. 56 | func Parse(pkg *genbase.PackageInfo, typeInfos genbase.TypeInfos) (*BuildSource, error) { 57 | bu := &BuildSource{ 58 | g: genbase.NewGenerator(pkg), 59 | pkg: pkg, 60 | typeInfos: typeInfos, 61 | } 62 | 63 | bu.g.AddImport("google.golang.org/appengine/v2/datastore", "") 64 | bu.g.AddImport("github.com/favclip/qbg/v2/qbgutils", "") 65 | 66 | for _, typeInfo := range typeInfos { 67 | err := bu.parseStruct(typeInfo) 68 | if err != nil { 69 | return nil, err 70 | } 71 | } 72 | 73 | return bu, nil 74 | } 75 | 76 | // Parse construct *BuildSource from package & type information. 77 | func (b *BuildSource) Parse(pkg *genbase.PackageInfo, typeInfos genbase.TypeInfos) error { 78 | if b.g == nil { 79 | b.g = genbase.NewGenerator(pkg) 80 | } 81 | b.pkg = pkg 82 | b.typeInfos = typeInfos 83 | 84 | if b.UseDatastoreWrapper { 85 | b.g.AddImport("go.mercari.io/datastore/v2", "") 86 | } else { 87 | b.g.AddImport("google.golang.org/appengine/v2/datastore", "") 88 | } 89 | if !b.InlineInterfaces { 90 | b.g.AddImport("github.com/favclip/qbg/v2/qbgutils", "") 91 | } 92 | 93 | for _, typeInfo := range typeInfos { 94 | err := b.parseStruct(typeInfo) 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (b *BuildSource) parseStruct(typeInfo *genbase.TypeInfo) error { 104 | structType, err := typeInfo.StructType() 105 | if err != nil { 106 | return err 107 | } 108 | 109 | st := &BuildStruct{ 110 | parent: b, 111 | typeInfo: typeInfo, 112 | } 113 | 114 | for _, fieldInfo := range structType.FieldInfos() { 115 | if len := len(fieldInfo.Names); len == 0 { 116 | // embedded struct in outer struct or multiply field declarations 117 | // https://play.golang.org/p/bcxbdiMyP4 118 | continue 119 | } 120 | 121 | for _, nameIdent := range fieldInfo.Names { 122 | err := b.parseField(st, typeInfo, fieldInfo, nameIdent.Name) 123 | if err != nil { 124 | return err 125 | } 126 | } 127 | } 128 | 129 | b.Structs = append(b.Structs, st) 130 | 131 | return nil 132 | } 133 | 134 | func (b *BuildSource) parseField(st *BuildStruct, typeInfo *genbase.TypeInfo, fieldInfo *genbase.FieldInfo, name string) error { 135 | field := &BuildField{ 136 | parent: st, 137 | fieldInfo: fieldInfo, 138 | Name: name, 139 | } 140 | st.Fields = append(st.Fields, field) 141 | 142 | tag := &BuildTag{ 143 | field: field, 144 | Name: name, 145 | } 146 | field.Tag = tag 147 | 148 | if fieldInfo.Tag != nil { 149 | // remove back quote 150 | tagBody := fieldInfo.Tag.Value[1 : len(fieldInfo.Tag.Value)-1] 151 | tagKeys := genbase.GetKeys(tagBody) 152 | structTag := reflect.StructTag(tagBody) 153 | for _, key := range tagKeys { 154 | if key == "datastore" { 155 | tagText := structTag.Get("datastore") 156 | if tagText == "-" && !tag.ID { 157 | tag.Ignore = true 158 | continue 159 | } 160 | if idx := strings.Index(tagText, ","); idx == -1 { 161 | tag.Name = tagText 162 | } else { 163 | for idx != -1 || tagText != "" { 164 | value := tagText 165 | if idx != -1 { 166 | value = tagText[:idx] 167 | tagText = tagText[idx+1:] 168 | } else { 169 | tagText = tagText[len(value):] 170 | } 171 | idx = strings.Index(tagText, ",") 172 | 173 | if value == "noindex" { 174 | tag.NoIndex = true 175 | } else if value != "" { 176 | tag.Name = value 177 | } 178 | } 179 | } 180 | } else if key == "goon" || key == "boom" { 181 | tagText := structTag.Get("goon") 182 | if tagText == "" { 183 | tagText = structTag.Get("boom") 184 | } 185 | if tagText == "id" { 186 | tag.ID = true 187 | tag.Ignore = false 188 | } else if strings.HasPrefix(tagText, "kind,") { 189 | tag.Kind = tagText[5:] 190 | } 191 | } else if key == "qbg" { 192 | tag.PropertyNameAlter = structTag.Get("qbg") 193 | } 194 | } 195 | } 196 | if tag.PropertyNameAlter == "" { 197 | switch field.Name { 198 | case "Ancestor", "KeysOnly", "Start", "Offset", "Limit", "Query": 199 | tag.PropertyNameAlter = field.Name + "Property" 200 | default: 201 | tag.PropertyNameAlter = field.Name 202 | } 203 | } 204 | 205 | return nil 206 | } 207 | 208 | // Emit generate wrapper code. 209 | func (b *BuildSource) Emit(args *[]string) ([]byte, error) { 210 | b.g.PrintHeader("qbg", args) 211 | 212 | if b.InlineInterfaces { 213 | tmpl := template.New("plugin") 214 | tmpl, err := tmpl.Parse(pluginTemplate) 215 | if err != nil { 216 | return nil, err 217 | } 218 | 219 | var dsKeyType string 220 | if b.UseDatastoreWrapper { 221 | dsKeyType = "datastore.Key" 222 | } else { 223 | dsKeyType = "*datastore.Key" 224 | } 225 | 226 | buf := bytes.NewBufferString("") 227 | err = tmpl.Execute(buf, map[string]string{ 228 | "DSKeyType": dsKeyType, 229 | }) 230 | if err != nil { 231 | return nil, err 232 | } 233 | 234 | b.g.Printf(buf.String()) 235 | } 236 | 237 | for _, st := range b.Structs { 238 | err := st.emit(b.g) 239 | if err != nil { 240 | return nil, err 241 | } 242 | } 243 | 244 | return b.g.Format() 245 | } 246 | 247 | func (st *BuildStruct) emit(g *genbase.Generator) error { 248 | tmpl := template.New("struct") 249 | tmpl, err := tmpl.Parse(structTemplate) 250 | if err != nil { 251 | return err 252 | } 253 | 254 | var newWord string 255 | if st.Private { 256 | newWord = "new" 257 | } else { 258 | newWord = "New" 259 | } 260 | var pluginType string 261 | if st.parent.InlineInterfaces { 262 | pluginType = "Plugin" 263 | } else { 264 | pluginType = "qbgutils.Plugin" 265 | } 266 | var pluggerType string 267 | if st.parent.InlineInterfaces { 268 | pluggerType = "Plugger" 269 | } else { 270 | pluggerType = "qbgutils.Plugger" 271 | } 272 | var fields []*BuildField 273 | for _, f := range st.Fields { 274 | if f.Tag.Ignore { 275 | continue 276 | } 277 | fields = append(fields, f) 278 | } 279 | var dsKeyType string 280 | var dsQueryType string 281 | var dsNewQuery string 282 | if st.parent.UseDatastoreWrapper { 283 | dsKeyType = "datastore.Key" 284 | dsQueryType = "datastore.Query" 285 | dsNewQuery = "client.NewQuery" 286 | } else { 287 | dsKeyType = "*datastore.Key" 288 | dsQueryType = "*datastore.Query" 289 | dsNewQuery = "datastore.NewQuery" 290 | } 291 | buf := bytes.NewBufferString("") 292 | err = tmpl.Execute(buf, map[string]interface{}{ 293 | "UserDatastoreWrapper": st.parent.UseDatastoreWrapper, 294 | "Name": st.Name(), 295 | "SimpleName": st.SimpleName(), 296 | "Kind": st.Kind(), 297 | "NewWord": newWord, 298 | "DSKeyType": dsKeyType, 299 | "DSQueryType": dsQueryType, 300 | "DSNewQuery": dsNewQuery, 301 | "PluginType": pluginType, 302 | "PluggerType": pluggerType, 303 | "Fields": fields, 304 | }) 305 | if err != nil { 306 | return err 307 | } 308 | 309 | g.Printf(buf.String()) 310 | 311 | return nil 312 | } 313 | 314 | // Name returns struct type name. 315 | func (st *BuildStruct) Name() string { 316 | // FooBar -> fooBar 317 | // IIS -> iis 318 | // AEData -> aeData 319 | 320 | name := st.SimpleName() 321 | 322 | if !st.Private { 323 | return name 324 | } 325 | if len(name) <= 1 { 326 | return strings.ToLower(name) 327 | } 328 | if name == strings.ToUpper(name) { 329 | return strings.ToLower(name) 330 | } 331 | 332 | runeNames := []rune(name) 333 | if unicode.IsLower(runeNames[0]) { 334 | return name 335 | } else if unicode.IsLower(runeNames[1]) { 336 | return string(unicode.ToLower(runeNames[0])) + string(runeNames[1:]) 337 | } 338 | 339 | var idx int 340 | for idx = 0; idx < len(runeNames); idx++ { 341 | r := runeNames[idx] 342 | if unicode.IsLower(r) { 343 | break 344 | } 345 | } 346 | 347 | return strings.ToLower(string(runeNames[0:idx-1])) + string(runeNames[idx-1:]) 348 | } 349 | 350 | // Name returns struct type name. 351 | func (st *BuildStruct) SimpleName() string { 352 | return st.typeInfo.Name() 353 | } 354 | 355 | // Kind returns kind from struct. 356 | func (st *BuildStruct) Kind() string { 357 | for _, field := range st.Fields { 358 | if field.Tag.Kind != "" { 359 | return field.Tag.Kind 360 | } 361 | } 362 | return st.Name() 363 | } 364 | -------------------------------------------------------------------------------- /v2/generator_test.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | 7 | "github.com/favclip/genbase" 8 | ) 9 | 10 | func TestGeneratorParsePackageDir(t *testing.T) { 11 | 12 | p := &genbase.Parser{SkipSemanticsCheck: true} 13 | pInfo, err := p.ParsePackageDir("./misc/fixture/a") 14 | if err != nil { 15 | t.Log(err) 16 | t.Fail() 17 | } 18 | 19 | if pInfo.Name() != "a" { 20 | t.Log("package name is not a, actual", pInfo.Name()) 21 | t.Fail() 22 | } 23 | if len(pInfo.Files) != 2 { 24 | t.Log("package files length is not 2, actual", len(pInfo.Files)) 25 | t.Fail() 26 | } 27 | if pInfo.Dir != "./misc/fixture/a" { 28 | t.Log("package dir is not ./misc/fixture/a, actual", pInfo.Dir) 29 | t.Fail() 30 | } 31 | } 32 | 33 | func TestGeneratorParsePackageFiles(t *testing.T) { 34 | p := &genbase.Parser{SkipSemanticsCheck: true} 35 | pInfo, err := p.ParsePackageFiles([]string{"./misc/fixture/a/model.go"}) 36 | if err != nil { 37 | t.Log(err) 38 | t.Fail() 39 | } 40 | 41 | if pInfo.Name() != "a" { 42 | t.Log("package name is not a, actual", pInfo.Name()) 43 | t.Fail() 44 | } 45 | if len(pInfo.Files) != 1 { 46 | t.Log("package files length is not 1, actual", len(pInfo.Files)) 47 | t.Fail() 48 | } 49 | if pInfo.Dir != "." { 50 | t.Log("package dir is not ., actual", pInfo.Dir) 51 | t.Fail() 52 | } 53 | } 54 | 55 | func TestGeneratorGenerate(t *testing.T) { 56 | 57 | testCase := []string{"a", "b"} 58 | for _, postFix := range testCase { 59 | p := &genbase.Parser{SkipSemanticsCheck: true} 60 | pInfo, err := p.ParsePackageDir("./misc/fixture/" + postFix) 61 | if err != nil { 62 | t.Log(err) 63 | t.Fail() 64 | } 65 | args := []string{"-type", "Sample", "-output", "misc/fixture/" + postFix + "/model_query.go", "misc/fixture/" + postFix} 66 | typeNames := []string{"Sample"} 67 | if postFix == "g" { 68 | args = []string{"-type", "Sample,Inner", "-output", "misc/fixture/" + postFix + "/model_query.go", "misc/fixture/" + postFix} 69 | typeNames = []string{"Sample", "Inner"} 70 | } 71 | 72 | bu, err := Parse(pInfo, pInfo.CollectTypeInfos(typeNames)) 73 | if err != nil { 74 | t.Log(err) 75 | t.Fail() 76 | } 77 | src, err := bu.Emit(&args) 78 | if err != nil { 79 | t.Log(err) 80 | t.Fail() 81 | } 82 | expected, err := ioutil.ReadFile("./misc/fixture/" + postFix + "/model_query.go") 83 | if err != nil { 84 | t.Log(err) 85 | t.Fail() 86 | } 87 | if string(src) != string(expected) { 88 | t.Log("not emit expected code in "+postFix+", actual\n", string(src)) 89 | t.Fail() 90 | } 91 | } 92 | } 93 | 94 | func TestCollectTaggedTypes(t *testing.T) { 95 | p := &genbase.Parser{SkipSemanticsCheck: true} 96 | pInfo, err := p.ParsePackageFiles([]string{"./misc/fixture/c/model.go"}) 97 | if err != nil { 98 | t.Log(err) 99 | t.Fail() 100 | } 101 | 102 | names := pInfo.CollectTaggedTypeInfos("+qbg") 103 | if len(names) != 1 { 104 | t.Log("names length is not 1, actual", len(names)) 105 | t.Fail() 106 | } 107 | if names[0].Name() != "Sample" { 108 | t.Log("name[0] is not \"Sample\", actual", names[0].Name()) 109 | t.Fail() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/favclip/qbg/v2 2 | 3 | go 1.11 4 | 5 | require ( 6 | github.com/favclip/genbase v1.1.0 7 | go.mercari.io/datastore/v2 v2.0.0 8 | google.golang.org/appengine/v2 v2.0.1 9 | ) 10 | -------------------------------------------------------------------------------- /v2/misc/fixture/a/model.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | // test for basic struct definition 4 | 5 | type Sample struct { 6 | Foo string 7 | } 8 | -------------------------------------------------------------------------------- /v2/misc/fixture/a/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -type Sample -output misc/fixture/a/model_query.go misc/fixture/a; DO NOT EDIT 2 | 3 | package a 4 | 5 | import ( 6 | "github.com/favclip/qbg/v2/qbgutils" 7 | "google.golang.org/appengine/v2/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Foo *SampleQueryProperty 15 | } 16 | 17 | // SampleQueryProperty has property information for SampleQueryBuilder. 18 | type SampleQueryProperty struct { 19 | bldr *SampleQueryBuilder 20 | name string 21 | } 22 | 23 | // NewSampleQueryBuilder create new SampleQueryBuilder. 24 | func NewSampleQueryBuilder() *SampleQueryBuilder { 25 | return NewSampleQueryBuilderWithKind("Sample") 26 | } 27 | 28 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 29 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 30 | q := datastore.NewQuery(kind) 31 | bldr := &SampleQueryBuilder{q: q} 32 | bldr.Foo = &SampleQueryProperty{ 33 | bldr: bldr, 34 | name: "Foo", 35 | } 36 | 37 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 38 | bldr.plugin = plugger.Plugin() 39 | bldr.plugin.Init("Sample") 40 | } 41 | 42 | return bldr 43 | } 44 | 45 | // Ancestor sets parent key to ancestor query. 46 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 47 | bldr.q = bldr.q.Ancestor(parentKey) 48 | if bldr.plugin != nil { 49 | bldr.plugin.Ancestor(parentKey) 50 | } 51 | return bldr 52 | } 53 | 54 | // KeysOnly sets keys only option to query. 55 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 56 | bldr.q = bldr.q.KeysOnly() 57 | if bldr.plugin != nil { 58 | bldr.plugin.KeysOnly() 59 | } 60 | return bldr 61 | } 62 | 63 | // Start setup to query. 64 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 65 | bldr.q = bldr.q.Start(cur) 66 | if bldr.plugin != nil { 67 | bldr.plugin.Start(cur) 68 | } 69 | return bldr 70 | } 71 | 72 | // Offset setup to query. 73 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 74 | bldr.q = bldr.q.Offset(offset) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Offset(offset) 77 | } 78 | return bldr 79 | } 80 | 81 | // Limit setup to query. 82 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 83 | bldr.q = bldr.q.Limit(limit) 84 | if bldr.plugin != nil { 85 | bldr.plugin.Limit(limit) 86 | } 87 | return bldr 88 | } 89 | 90 | // Query returns *datastore.Query. 91 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 92 | return bldr.q 93 | } 94 | 95 | // Filter with op & value. 96 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 97 | switch op { 98 | case "<=": 99 | p.LessThanOrEqual(value) 100 | case ">=": 101 | p.GreaterThanOrEqual(value) 102 | case "<": 103 | p.LessThan(value) 104 | case ">": 105 | p.GreaterThan(value) 106 | case "=": 107 | p.Equal(value) 108 | default: 109 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 110 | } 111 | if p.bldr.plugin != nil { 112 | p.bldr.plugin.Filter(p.name, op, value) 113 | } 114 | return p.bldr 115 | } 116 | 117 | // LessThanOrEqual filter with value. 118 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 119 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 120 | if p.bldr.plugin != nil { 121 | p.bldr.plugin.Filter(p.name, "<=", value) 122 | } 123 | return p.bldr 124 | } 125 | 126 | // GreaterThanOrEqual filter with value. 127 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 128 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 129 | if p.bldr.plugin != nil { 130 | p.bldr.plugin.Filter(p.name, ">=", value) 131 | } 132 | return p.bldr 133 | } 134 | 135 | // LessThan filter with value. 136 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 137 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, "<", value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // GreaterThan filter with value. 145 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, ">", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // Equal filter with value. 154 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, "=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // Asc order. 163 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 164 | p.bldr.q = p.bldr.q.Order(p.name) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Asc(p.name) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // Desc order. 172 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 173 | p.bldr.q = p.bldr.q.Order("-" + p.name) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Desc(p.name) 176 | } 177 | return p.bldr 178 | } 179 | -------------------------------------------------------------------------------- /v2/misc/fixture/b/model.go: -------------------------------------------------------------------------------- 1 | package b 2 | 3 | import "time" 4 | 5 | type Sample struct { 6 | ID int64 `datastore:"-" goon:"id"` 7 | CreatedAt time.Time `datastore:",noindex"` 8 | } 9 | -------------------------------------------------------------------------------- /v2/misc/fixture/b/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -type Sample -output misc/fixture/b/model_query.go misc/fixture/b; DO NOT EDIT 2 | 3 | package b 4 | 5 | import ( 6 | "github.com/favclip/qbg/v2/qbgutils" 7 | "google.golang.org/appengine/v2/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | ID *SampleQueryProperty 15 | CreatedAt *SampleQueryProperty 16 | } 17 | 18 | // SampleQueryProperty has property information for SampleQueryBuilder. 19 | type SampleQueryProperty struct { 20 | bldr *SampleQueryBuilder 21 | name string 22 | } 23 | 24 | // NewSampleQueryBuilder create new SampleQueryBuilder. 25 | func NewSampleQueryBuilder() *SampleQueryBuilder { 26 | return NewSampleQueryBuilderWithKind("Sample") 27 | } 28 | 29 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 30 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 31 | q := datastore.NewQuery(kind) 32 | bldr := &SampleQueryBuilder{q: q} 33 | bldr.ID = &SampleQueryProperty{ 34 | bldr: bldr, 35 | name: "__key__", 36 | } 37 | bldr.CreatedAt = &SampleQueryProperty{ 38 | bldr: bldr, 39 | name: "CreatedAt", 40 | } 41 | 42 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 43 | bldr.plugin = plugger.Plugin() 44 | bldr.plugin.Init("Sample") 45 | } 46 | 47 | return bldr 48 | } 49 | 50 | // Ancestor sets parent key to ancestor query. 51 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 52 | bldr.q = bldr.q.Ancestor(parentKey) 53 | if bldr.plugin != nil { 54 | bldr.plugin.Ancestor(parentKey) 55 | } 56 | return bldr 57 | } 58 | 59 | // KeysOnly sets keys only option to query. 60 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 61 | bldr.q = bldr.q.KeysOnly() 62 | if bldr.plugin != nil { 63 | bldr.plugin.KeysOnly() 64 | } 65 | return bldr 66 | } 67 | 68 | // Start setup to query. 69 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 70 | bldr.q = bldr.q.Start(cur) 71 | if bldr.plugin != nil { 72 | bldr.plugin.Start(cur) 73 | } 74 | return bldr 75 | } 76 | 77 | // Offset setup to query. 78 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 79 | bldr.q = bldr.q.Offset(offset) 80 | if bldr.plugin != nil { 81 | bldr.plugin.Offset(offset) 82 | } 83 | return bldr 84 | } 85 | 86 | // Limit setup to query. 87 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 88 | bldr.q = bldr.q.Limit(limit) 89 | if bldr.plugin != nil { 90 | bldr.plugin.Limit(limit) 91 | } 92 | return bldr 93 | } 94 | 95 | // Query returns *datastore.Query. 96 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 97 | return bldr.q 98 | } 99 | 100 | // Filter with op & value. 101 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 102 | switch op { 103 | case "<=": 104 | p.LessThanOrEqual(value) 105 | case ">=": 106 | p.GreaterThanOrEqual(value) 107 | case "<": 108 | p.LessThan(value) 109 | case ">": 110 | p.GreaterThan(value) 111 | case "=": 112 | p.Equal(value) 113 | default: 114 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 115 | } 116 | if p.bldr.plugin != nil { 117 | p.bldr.plugin.Filter(p.name, op, value) 118 | } 119 | return p.bldr 120 | } 121 | 122 | // LessThanOrEqual filter with value. 123 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 124 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 125 | if p.bldr.plugin != nil { 126 | p.bldr.plugin.Filter(p.name, "<=", value) 127 | } 128 | return p.bldr 129 | } 130 | 131 | // GreaterThanOrEqual filter with value. 132 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 133 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 134 | if p.bldr.plugin != nil { 135 | p.bldr.plugin.Filter(p.name, ">=", value) 136 | } 137 | return p.bldr 138 | } 139 | 140 | // LessThan filter with value. 141 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 142 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 143 | if p.bldr.plugin != nil { 144 | p.bldr.plugin.Filter(p.name, "<", value) 145 | } 146 | return p.bldr 147 | } 148 | 149 | // GreaterThan filter with value. 150 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 151 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 152 | if p.bldr.plugin != nil { 153 | p.bldr.plugin.Filter(p.name, ">", value) 154 | } 155 | return p.bldr 156 | } 157 | 158 | // Equal filter with value. 159 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 160 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 161 | if p.bldr.plugin != nil { 162 | p.bldr.plugin.Filter(p.name, "=", value) 163 | } 164 | return p.bldr 165 | } 166 | 167 | // Asc order. 168 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 169 | p.bldr.q = p.bldr.q.Order(p.name) 170 | if p.bldr.plugin != nil { 171 | p.bldr.plugin.Asc(p.name) 172 | } 173 | return p.bldr 174 | } 175 | 176 | // Desc order. 177 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 178 | p.bldr.q = p.bldr.q.Order("-" + p.name) 179 | if p.bldr.plugin != nil { 180 | p.bldr.plugin.Desc(p.name) 181 | } 182 | return p.bldr 183 | } 184 | -------------------------------------------------------------------------------- /v2/misc/fixture/c/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go . 2 | 3 | package c 4 | 5 | import "time" 6 | 7 | // +qbg 8 | type Sample struct { 9 | ID int64 `goon:"id"` 10 | CreatedAt time.Time `datastore:",noindex"` 11 | } 12 | -------------------------------------------------------------------------------- /v2/misc/fixture/c/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -output misc/fixture/c/model_query.go misc/fixture/c; DO NOT EDIT 2 | 3 | package c 4 | 5 | import ( 6 | "github.com/favclip/qbg/v2/qbgutils" 7 | "google.golang.org/appengine/v2/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | ID *SampleQueryProperty 15 | CreatedAt *SampleQueryProperty 16 | } 17 | 18 | // SampleQueryProperty has property information for SampleQueryBuilder. 19 | type SampleQueryProperty struct { 20 | bldr *SampleQueryBuilder 21 | name string 22 | } 23 | 24 | // NewSampleQueryBuilder create new SampleQueryBuilder. 25 | func NewSampleQueryBuilder() *SampleQueryBuilder { 26 | return NewSampleQueryBuilderWithKind("Sample") 27 | } 28 | 29 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 30 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 31 | q := datastore.NewQuery(kind) 32 | bldr := &SampleQueryBuilder{q: q} 33 | bldr.ID = &SampleQueryProperty{ 34 | bldr: bldr, 35 | name: "__key__", 36 | } 37 | bldr.CreatedAt = &SampleQueryProperty{ 38 | bldr: bldr, 39 | name: "CreatedAt", 40 | } 41 | 42 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 43 | bldr.plugin = plugger.Plugin() 44 | bldr.plugin.Init("Sample") 45 | } 46 | 47 | return bldr 48 | } 49 | 50 | // Ancestor sets parent key to ancestor query. 51 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 52 | bldr.q = bldr.q.Ancestor(parentKey) 53 | if bldr.plugin != nil { 54 | bldr.plugin.Ancestor(parentKey) 55 | } 56 | return bldr 57 | } 58 | 59 | // KeysOnly sets keys only option to query. 60 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 61 | bldr.q = bldr.q.KeysOnly() 62 | if bldr.plugin != nil { 63 | bldr.plugin.KeysOnly() 64 | } 65 | return bldr 66 | } 67 | 68 | // Start setup to query. 69 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 70 | bldr.q = bldr.q.Start(cur) 71 | if bldr.plugin != nil { 72 | bldr.plugin.Start(cur) 73 | } 74 | return bldr 75 | } 76 | 77 | // Offset setup to query. 78 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 79 | bldr.q = bldr.q.Offset(offset) 80 | if bldr.plugin != nil { 81 | bldr.plugin.Offset(offset) 82 | } 83 | return bldr 84 | } 85 | 86 | // Limit setup to query. 87 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 88 | bldr.q = bldr.q.Limit(limit) 89 | if bldr.plugin != nil { 90 | bldr.plugin.Limit(limit) 91 | } 92 | return bldr 93 | } 94 | 95 | // Query returns *datastore.Query. 96 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 97 | return bldr.q 98 | } 99 | 100 | // Filter with op & value. 101 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 102 | switch op { 103 | case "<=": 104 | p.LessThanOrEqual(value) 105 | case ">=": 106 | p.GreaterThanOrEqual(value) 107 | case "<": 108 | p.LessThan(value) 109 | case ">": 110 | p.GreaterThan(value) 111 | case "=": 112 | p.Equal(value) 113 | default: 114 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 115 | } 116 | if p.bldr.plugin != nil { 117 | p.bldr.plugin.Filter(p.name, op, value) 118 | } 119 | return p.bldr 120 | } 121 | 122 | // LessThanOrEqual filter with value. 123 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 124 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 125 | if p.bldr.plugin != nil { 126 | p.bldr.plugin.Filter(p.name, "<=", value) 127 | } 128 | return p.bldr 129 | } 130 | 131 | // GreaterThanOrEqual filter with value. 132 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 133 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 134 | if p.bldr.plugin != nil { 135 | p.bldr.plugin.Filter(p.name, ">=", value) 136 | } 137 | return p.bldr 138 | } 139 | 140 | // LessThan filter with value. 141 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 142 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 143 | if p.bldr.plugin != nil { 144 | p.bldr.plugin.Filter(p.name, "<", value) 145 | } 146 | return p.bldr 147 | } 148 | 149 | // GreaterThan filter with value. 150 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 151 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 152 | if p.bldr.plugin != nil { 153 | p.bldr.plugin.Filter(p.name, ">", value) 154 | } 155 | return p.bldr 156 | } 157 | 158 | // Equal filter with value. 159 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 160 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 161 | if p.bldr.plugin != nil { 162 | p.bldr.plugin.Filter(p.name, "=", value) 163 | } 164 | return p.bldr 165 | } 166 | 167 | // Asc order. 168 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 169 | p.bldr.q = p.bldr.q.Order(p.name) 170 | if p.bldr.plugin != nil { 171 | p.bldr.plugin.Asc(p.name) 172 | } 173 | return p.bldr 174 | } 175 | 176 | // Desc order. 177 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 178 | p.bldr.q = p.bldr.q.Order("-" + p.name) 179 | if p.bldr.plugin != nil { 180 | p.bldr.plugin.Desc(p.name) 181 | } 182 | return p.bldr 183 | } 184 | -------------------------------------------------------------------------------- /v2/misc/fixture/d/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go . 2 | 3 | package c 4 | 5 | import "time" 6 | 7 | // +qbg 8 | type Sample struct { 9 | ID int64 `goon:"id"` 10 | FooUrl string `datastore:"FooURL,noindex"` 11 | Start time.Ticker `qbg:"StartAt"` 12 | Limit int 13 | CreatedAt time.Time `datastore:",noindex"` 14 | } 15 | -------------------------------------------------------------------------------- /v2/misc/fixture/d/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -output misc/fixture/d/model_query.go misc/fixture/d; DO NOT EDIT 2 | 3 | package c 4 | 5 | import ( 6 | "github.com/favclip/qbg/v2/qbgutils" 7 | "google.golang.org/appengine/v2/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | ID *SampleQueryProperty 15 | FooUrl *SampleQueryProperty 16 | StartAt *SampleQueryProperty 17 | LimitProperty *SampleQueryProperty 18 | CreatedAt *SampleQueryProperty 19 | } 20 | 21 | // SampleQueryProperty has property information for SampleQueryBuilder. 22 | type SampleQueryProperty struct { 23 | bldr *SampleQueryBuilder 24 | name string 25 | } 26 | 27 | // NewSampleQueryBuilder create new SampleQueryBuilder. 28 | func NewSampleQueryBuilder() *SampleQueryBuilder { 29 | return NewSampleQueryBuilderWithKind("Sample") 30 | } 31 | 32 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 33 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 34 | q := datastore.NewQuery(kind) 35 | bldr := &SampleQueryBuilder{q: q} 36 | bldr.ID = &SampleQueryProperty{ 37 | bldr: bldr, 38 | name: "__key__", 39 | } 40 | bldr.FooUrl = &SampleQueryProperty{ 41 | bldr: bldr, 42 | name: "FooURL", 43 | } 44 | bldr.StartAt = &SampleQueryProperty{ 45 | bldr: bldr, 46 | name: "Start", 47 | } 48 | bldr.LimitProperty = &SampleQueryProperty{ 49 | bldr: bldr, 50 | name: "Limit", 51 | } 52 | bldr.CreatedAt = &SampleQueryProperty{ 53 | bldr: bldr, 54 | name: "CreatedAt", 55 | } 56 | 57 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 58 | bldr.plugin = plugger.Plugin() 59 | bldr.plugin.Init("Sample") 60 | } 61 | 62 | return bldr 63 | } 64 | 65 | // Ancestor sets parent key to ancestor query. 66 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 67 | bldr.q = bldr.q.Ancestor(parentKey) 68 | if bldr.plugin != nil { 69 | bldr.plugin.Ancestor(parentKey) 70 | } 71 | return bldr 72 | } 73 | 74 | // KeysOnly sets keys only option to query. 75 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 76 | bldr.q = bldr.q.KeysOnly() 77 | if bldr.plugin != nil { 78 | bldr.plugin.KeysOnly() 79 | } 80 | return bldr 81 | } 82 | 83 | // Start setup to query. 84 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 85 | bldr.q = bldr.q.Start(cur) 86 | if bldr.plugin != nil { 87 | bldr.plugin.Start(cur) 88 | } 89 | return bldr 90 | } 91 | 92 | // Offset setup to query. 93 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 94 | bldr.q = bldr.q.Offset(offset) 95 | if bldr.plugin != nil { 96 | bldr.plugin.Offset(offset) 97 | } 98 | return bldr 99 | } 100 | 101 | // Limit setup to query. 102 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 103 | bldr.q = bldr.q.Limit(limit) 104 | if bldr.plugin != nil { 105 | bldr.plugin.Limit(limit) 106 | } 107 | return bldr 108 | } 109 | 110 | // Query returns *datastore.Query. 111 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 112 | return bldr.q 113 | } 114 | 115 | // Filter with op & value. 116 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 117 | switch op { 118 | case "<=": 119 | p.LessThanOrEqual(value) 120 | case ">=": 121 | p.GreaterThanOrEqual(value) 122 | case "<": 123 | p.LessThan(value) 124 | case ">": 125 | p.GreaterThan(value) 126 | case "=": 127 | p.Equal(value) 128 | default: 129 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 130 | } 131 | if p.bldr.plugin != nil { 132 | p.bldr.plugin.Filter(p.name, op, value) 133 | } 134 | return p.bldr 135 | } 136 | 137 | // LessThanOrEqual filter with value. 138 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 139 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 140 | if p.bldr.plugin != nil { 141 | p.bldr.plugin.Filter(p.name, "<=", value) 142 | } 143 | return p.bldr 144 | } 145 | 146 | // GreaterThanOrEqual filter with value. 147 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 148 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 149 | if p.bldr.plugin != nil { 150 | p.bldr.plugin.Filter(p.name, ">=", value) 151 | } 152 | return p.bldr 153 | } 154 | 155 | // LessThan filter with value. 156 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 157 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 158 | if p.bldr.plugin != nil { 159 | p.bldr.plugin.Filter(p.name, "<", value) 160 | } 161 | return p.bldr 162 | } 163 | 164 | // GreaterThan filter with value. 165 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 166 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 167 | if p.bldr.plugin != nil { 168 | p.bldr.plugin.Filter(p.name, ">", value) 169 | } 170 | return p.bldr 171 | } 172 | 173 | // Equal filter with value. 174 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 175 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 176 | if p.bldr.plugin != nil { 177 | p.bldr.plugin.Filter(p.name, "=", value) 178 | } 179 | return p.bldr 180 | } 181 | 182 | // Asc order. 183 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 184 | p.bldr.q = p.bldr.q.Order(p.name) 185 | if p.bldr.plugin != nil { 186 | p.bldr.plugin.Asc(p.name) 187 | } 188 | return p.bldr 189 | } 190 | 191 | // Desc order. 192 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 193 | p.bldr.q = p.bldr.q.Order("-" + p.name) 194 | if p.bldr.plugin != nil { 195 | p.bldr.plugin.Desc(p.name) 196 | } 197 | return p.bldr 198 | } 199 | -------------------------------------------------------------------------------- /v2/misc/fixture/e/model.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/favclip/qbg/v2/qbgutils" 8 | "google.golang.org/appengine/v2/datastore" 9 | ) 10 | 11 | // +qbg 12 | type Sample struct { 13 | Foo string 14 | } 15 | 16 | func (bldr *SampleQueryBuilder) Plugin() qbgutils.Plugin { 17 | if bldr.plugin == nil { 18 | bldr.plugin = &MemcacheQueryPlugin{} 19 | } 20 | return bldr.plugin 21 | } 22 | 23 | type MemcacheQueryPlugin struct { 24 | buf bytes.Buffer 25 | } 26 | 27 | func (p *MemcacheQueryPlugin) AddCounter(counter uint64) { 28 | p.buf.WriteString(fmt.Sprintf(":%d", counter)) 29 | } 30 | 31 | func (p *MemcacheQueryPlugin) Init(typeName string) { 32 | p.buf.WriteString(fmt.Sprintf("k=%s", typeName)) 33 | } 34 | 35 | func (p *MemcacheQueryPlugin) Ancestor(ancestor *datastore.Key) { 36 | p.buf.WriteString(fmt.Sprintf(":!a=%s", ancestor.String())) 37 | } 38 | 39 | func (p *MemcacheQueryPlugin) KeysOnly() { 40 | p.buf.WriteString(":!k") 41 | } 42 | 43 | func (p *MemcacheQueryPlugin) Start(cur datastore.Cursor) { 44 | p.buf.WriteString(fmt.Sprintf(":!s=%s", cur.String())) 45 | } 46 | 47 | func (p *MemcacheQueryPlugin) Offset(offset int) { 48 | p.buf.WriteString(fmt.Sprintf(":!o=%d", offset)) 49 | } 50 | 51 | func (p *MemcacheQueryPlugin) Limit(limit int) { 52 | p.buf.WriteString(fmt.Sprintf(":!l=%d", limit)) 53 | } 54 | 55 | func (p *MemcacheQueryPlugin) Filter(name, op string, value interface{}) { 56 | p.buf.WriteString(fmt.Sprintf(":?%s%s%#v", name, op, value)) 57 | } 58 | 59 | func (p *MemcacheQueryPlugin) Asc(name string) { 60 | p.buf.WriteString(fmt.Sprintf(":A=%s", name)) 61 | } 62 | 63 | func (p *MemcacheQueryPlugin) Desc(name string) { 64 | p.buf.WriteString(fmt.Sprintf(":D=%s", name)) 65 | } 66 | 67 | func (p *MemcacheQueryPlugin) QueryString() string { 68 | return p.buf.String() 69 | } 70 | -------------------------------------------------------------------------------- /v2/misc/fixture/e/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -output misc/fixture/e/model_query.go misc/fixture/e; DO NOT EDIT 2 | 3 | package e 4 | 5 | import ( 6 | "github.com/favclip/qbg/v2/qbgutils" 7 | "google.golang.org/appengine/v2/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Foo *SampleQueryProperty 15 | } 16 | 17 | // SampleQueryProperty has property information for SampleQueryBuilder. 18 | type SampleQueryProperty struct { 19 | bldr *SampleQueryBuilder 20 | name string 21 | } 22 | 23 | // NewSampleQueryBuilder create new SampleQueryBuilder. 24 | func NewSampleQueryBuilder() *SampleQueryBuilder { 25 | return NewSampleQueryBuilderWithKind("Sample") 26 | } 27 | 28 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 29 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 30 | q := datastore.NewQuery(kind) 31 | bldr := &SampleQueryBuilder{q: q} 32 | bldr.Foo = &SampleQueryProperty{ 33 | bldr: bldr, 34 | name: "Foo", 35 | } 36 | 37 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 38 | bldr.plugin = plugger.Plugin() 39 | bldr.plugin.Init("Sample") 40 | } 41 | 42 | return bldr 43 | } 44 | 45 | // Ancestor sets parent key to ancestor query. 46 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 47 | bldr.q = bldr.q.Ancestor(parentKey) 48 | if bldr.plugin != nil { 49 | bldr.plugin.Ancestor(parentKey) 50 | } 51 | return bldr 52 | } 53 | 54 | // KeysOnly sets keys only option to query. 55 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 56 | bldr.q = bldr.q.KeysOnly() 57 | if bldr.plugin != nil { 58 | bldr.plugin.KeysOnly() 59 | } 60 | return bldr 61 | } 62 | 63 | // Start setup to query. 64 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 65 | bldr.q = bldr.q.Start(cur) 66 | if bldr.plugin != nil { 67 | bldr.plugin.Start(cur) 68 | } 69 | return bldr 70 | } 71 | 72 | // Offset setup to query. 73 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 74 | bldr.q = bldr.q.Offset(offset) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Offset(offset) 77 | } 78 | return bldr 79 | } 80 | 81 | // Limit setup to query. 82 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 83 | bldr.q = bldr.q.Limit(limit) 84 | if bldr.plugin != nil { 85 | bldr.plugin.Limit(limit) 86 | } 87 | return bldr 88 | } 89 | 90 | // Query returns *datastore.Query. 91 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 92 | return bldr.q 93 | } 94 | 95 | // Filter with op & value. 96 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 97 | switch op { 98 | case "<=": 99 | p.LessThanOrEqual(value) 100 | case ">=": 101 | p.GreaterThanOrEqual(value) 102 | case "<": 103 | p.LessThan(value) 104 | case ">": 105 | p.GreaterThan(value) 106 | case "=": 107 | p.Equal(value) 108 | default: 109 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 110 | } 111 | if p.bldr.plugin != nil { 112 | p.bldr.plugin.Filter(p.name, op, value) 113 | } 114 | return p.bldr 115 | } 116 | 117 | // LessThanOrEqual filter with value. 118 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 119 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 120 | if p.bldr.plugin != nil { 121 | p.bldr.plugin.Filter(p.name, "<=", value) 122 | } 123 | return p.bldr 124 | } 125 | 126 | // GreaterThanOrEqual filter with value. 127 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 128 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 129 | if p.bldr.plugin != nil { 130 | p.bldr.plugin.Filter(p.name, ">=", value) 131 | } 132 | return p.bldr 133 | } 134 | 135 | // LessThan filter with value. 136 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 137 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, "<", value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // GreaterThan filter with value. 145 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, ">", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // Equal filter with value. 154 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, "=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // Asc order. 163 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 164 | p.bldr.q = p.bldr.q.Order(p.name) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Asc(p.name) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // Desc order. 172 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 173 | p.bldr.q = p.bldr.q.Order("-" + p.name) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Desc(p.name) 176 | } 177 | return p.bldr 178 | } 179 | -------------------------------------------------------------------------------- /v2/misc/fixture/e/model_test.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | import "testing" 4 | 5 | func TestPlugin(t *testing.T) { 6 | qb := NewSampleQueryBuilder() 7 | qb.Foo.Equal("test") 8 | qb.Foo.Desc() 9 | qb.KeysOnly() 10 | qb.Offset(0) 11 | qb.Limit(10) 12 | 13 | mp, ok := qb.Plugin().(*MemcacheQueryPlugin) 14 | if !ok { 15 | t.Fatal("Plugin is not MemcacheQueryPlugin") 16 | } 17 | 18 | if str := mp.QueryString(); str != `k=Sample:?Foo="test":D=Foo:!k:!o=0:!l=10` { 19 | t.Fatalf("unexpected: %s", str) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /v2/misc/fixture/f/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go . 2 | package f 3 | 4 | // +qbg 5 | type Sample struct { 6 | Kind string `goon:"kind,sample_kind"` 7 | Foo string 8 | } 9 | -------------------------------------------------------------------------------- /v2/misc/fixture/f/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -output misc/fixture/f/model_query.go misc/fixture/f; DO NOT EDIT 2 | 3 | package f 4 | 5 | import ( 6 | "github.com/favclip/qbg/v2/qbgutils" 7 | "google.golang.org/appengine/v2/datastore" 8 | ) 9 | 10 | // SampleQueryBuilder build query for Sample. 11 | type SampleQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Kind *SampleQueryProperty 15 | Foo *SampleQueryProperty 16 | } 17 | 18 | // SampleQueryProperty has property information for SampleQueryBuilder. 19 | type SampleQueryProperty struct { 20 | bldr *SampleQueryBuilder 21 | name string 22 | } 23 | 24 | // NewSampleQueryBuilder create new SampleQueryBuilder. 25 | func NewSampleQueryBuilder() *SampleQueryBuilder { 26 | return NewSampleQueryBuilderWithKind("sample_kind") 27 | } 28 | 29 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 30 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 31 | q := datastore.NewQuery(kind) 32 | bldr := &SampleQueryBuilder{q: q} 33 | bldr.Kind = &SampleQueryProperty{ 34 | bldr: bldr, 35 | name: "Kind", 36 | } 37 | bldr.Foo = &SampleQueryProperty{ 38 | bldr: bldr, 39 | name: "Foo", 40 | } 41 | 42 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 43 | bldr.plugin = plugger.Plugin() 44 | bldr.plugin.Init("Sample") 45 | } 46 | 47 | return bldr 48 | } 49 | 50 | // Ancestor sets parent key to ancestor query. 51 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 52 | bldr.q = bldr.q.Ancestor(parentKey) 53 | if bldr.plugin != nil { 54 | bldr.plugin.Ancestor(parentKey) 55 | } 56 | return bldr 57 | } 58 | 59 | // KeysOnly sets keys only option to query. 60 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 61 | bldr.q = bldr.q.KeysOnly() 62 | if bldr.plugin != nil { 63 | bldr.plugin.KeysOnly() 64 | } 65 | return bldr 66 | } 67 | 68 | // Start setup to query. 69 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 70 | bldr.q = bldr.q.Start(cur) 71 | if bldr.plugin != nil { 72 | bldr.plugin.Start(cur) 73 | } 74 | return bldr 75 | } 76 | 77 | // Offset setup to query. 78 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 79 | bldr.q = bldr.q.Offset(offset) 80 | if bldr.plugin != nil { 81 | bldr.plugin.Offset(offset) 82 | } 83 | return bldr 84 | } 85 | 86 | // Limit setup to query. 87 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 88 | bldr.q = bldr.q.Limit(limit) 89 | if bldr.plugin != nil { 90 | bldr.plugin.Limit(limit) 91 | } 92 | return bldr 93 | } 94 | 95 | // Query returns *datastore.Query. 96 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 97 | return bldr.q 98 | } 99 | 100 | // Filter with op & value. 101 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 102 | switch op { 103 | case "<=": 104 | p.LessThanOrEqual(value) 105 | case ">=": 106 | p.GreaterThanOrEqual(value) 107 | case "<": 108 | p.LessThan(value) 109 | case ">": 110 | p.GreaterThan(value) 111 | case "=": 112 | p.Equal(value) 113 | default: 114 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 115 | } 116 | if p.bldr.plugin != nil { 117 | p.bldr.plugin.Filter(p.name, op, value) 118 | } 119 | return p.bldr 120 | } 121 | 122 | // LessThanOrEqual filter with value. 123 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 124 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 125 | if p.bldr.plugin != nil { 126 | p.bldr.plugin.Filter(p.name, "<=", value) 127 | } 128 | return p.bldr 129 | } 130 | 131 | // GreaterThanOrEqual filter with value. 132 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 133 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 134 | if p.bldr.plugin != nil { 135 | p.bldr.plugin.Filter(p.name, ">=", value) 136 | } 137 | return p.bldr 138 | } 139 | 140 | // LessThan filter with value. 141 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 142 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 143 | if p.bldr.plugin != nil { 144 | p.bldr.plugin.Filter(p.name, "<", value) 145 | } 146 | return p.bldr 147 | } 148 | 149 | // GreaterThan filter with value. 150 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 151 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 152 | if p.bldr.plugin != nil { 153 | p.bldr.plugin.Filter(p.name, ">", value) 154 | } 155 | return p.bldr 156 | } 157 | 158 | // Equal filter with value. 159 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 160 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 161 | if p.bldr.plugin != nil { 162 | p.bldr.plugin.Filter(p.name, "=", value) 163 | } 164 | return p.bldr 165 | } 166 | 167 | // Asc order. 168 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 169 | p.bldr.q = p.bldr.q.Order(p.name) 170 | if p.bldr.plugin != nil { 171 | p.bldr.plugin.Asc(p.name) 172 | } 173 | return p.bldr 174 | } 175 | 176 | // Desc order. 177 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 178 | p.bldr.q = p.bldr.q.Order("-" + p.name) 179 | if p.bldr.plugin != nil { 180 | p.bldr.plugin.Desc(p.name) 181 | } 182 | return p.bldr 183 | } 184 | -------------------------------------------------------------------------------- /v2/misc/fixture/g/model.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | // +qbg 4 | type FooBar struct { 5 | Foo string 6 | } 7 | 8 | // +qbg 9 | type AEData struct { 10 | Foo string 11 | } 12 | 13 | // +qbg 14 | type IIS struct { 15 | Foo string 16 | } 17 | -------------------------------------------------------------------------------- /v2/misc/fixture/g/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -output misc/fixture/g/model_query.go -private misc/fixture/g; DO NOT EDIT 2 | 3 | package g 4 | 5 | import ( 6 | "github.com/favclip/qbg/v2/qbgutils" 7 | "google.golang.org/appengine/v2/datastore" 8 | ) 9 | 10 | // fooBarQueryBuilder build query for fooBar. 11 | type fooBarQueryBuilder struct { 12 | q *datastore.Query 13 | plugin qbgutils.Plugin 14 | Foo *fooBarQueryProperty 15 | } 16 | 17 | // fooBarQueryProperty has property information for fooBarQueryBuilder. 18 | type fooBarQueryProperty struct { 19 | bldr *fooBarQueryBuilder 20 | name string 21 | } 22 | 23 | // newFooBarQueryBuilder create new FooBarQueryBuilder. 24 | func newFooBarQueryBuilder() *fooBarQueryBuilder { 25 | return newFooBarQueryBuilderWithKind("fooBar") 26 | } 27 | 28 | // newFooBarQueryBuilderWithKind create new FooBarQueryBuilder with specific kind. 29 | func newFooBarQueryBuilderWithKind(kind string) *fooBarQueryBuilder { 30 | q := datastore.NewQuery(kind) 31 | bldr := &fooBarQueryBuilder{q: q} 32 | bldr.Foo = &fooBarQueryProperty{ 33 | bldr: bldr, 34 | name: "Foo", 35 | } 36 | 37 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 38 | bldr.plugin = plugger.Plugin() 39 | bldr.plugin.Init("FooBar") 40 | } 41 | 42 | return bldr 43 | } 44 | 45 | // Ancestor sets parent key to ancestor query. 46 | func (bldr *fooBarQueryBuilder) Ancestor(parentKey *datastore.Key) *fooBarQueryBuilder { 47 | bldr.q = bldr.q.Ancestor(parentKey) 48 | if bldr.plugin != nil { 49 | bldr.plugin.Ancestor(parentKey) 50 | } 51 | return bldr 52 | } 53 | 54 | // KeysOnly sets keys only option to query. 55 | func (bldr *fooBarQueryBuilder) KeysOnly() *fooBarQueryBuilder { 56 | bldr.q = bldr.q.KeysOnly() 57 | if bldr.plugin != nil { 58 | bldr.plugin.KeysOnly() 59 | } 60 | return bldr 61 | } 62 | 63 | // Start setup to query. 64 | func (bldr *fooBarQueryBuilder) Start(cur datastore.Cursor) *fooBarQueryBuilder { 65 | bldr.q = bldr.q.Start(cur) 66 | if bldr.plugin != nil { 67 | bldr.plugin.Start(cur) 68 | } 69 | return bldr 70 | } 71 | 72 | // Offset setup to query. 73 | func (bldr *fooBarQueryBuilder) Offset(offset int) *fooBarQueryBuilder { 74 | bldr.q = bldr.q.Offset(offset) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Offset(offset) 77 | } 78 | return bldr 79 | } 80 | 81 | // Limit setup to query. 82 | func (bldr *fooBarQueryBuilder) Limit(limit int) *fooBarQueryBuilder { 83 | bldr.q = bldr.q.Limit(limit) 84 | if bldr.plugin != nil { 85 | bldr.plugin.Limit(limit) 86 | } 87 | return bldr 88 | } 89 | 90 | // Query returns *datastore.Query. 91 | func (bldr *fooBarQueryBuilder) Query() *datastore.Query { 92 | return bldr.q 93 | } 94 | 95 | // Filter with op & value. 96 | func (p *fooBarQueryProperty) Filter(op string, value interface{}) *fooBarQueryBuilder { 97 | switch op { 98 | case "<=": 99 | p.LessThanOrEqual(value) 100 | case ">=": 101 | p.GreaterThanOrEqual(value) 102 | case "<": 103 | p.LessThan(value) 104 | case ">": 105 | p.GreaterThan(value) 106 | case "=": 107 | p.Equal(value) 108 | default: 109 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 110 | } 111 | if p.bldr.plugin != nil { 112 | p.bldr.plugin.Filter(p.name, op, value) 113 | } 114 | return p.bldr 115 | } 116 | 117 | // LessThanOrEqual filter with value. 118 | func (p *fooBarQueryProperty) LessThanOrEqual(value interface{}) *fooBarQueryBuilder { 119 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 120 | if p.bldr.plugin != nil { 121 | p.bldr.plugin.Filter(p.name, "<=", value) 122 | } 123 | return p.bldr 124 | } 125 | 126 | // GreaterThanOrEqual filter with value. 127 | func (p *fooBarQueryProperty) GreaterThanOrEqual(value interface{}) *fooBarQueryBuilder { 128 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 129 | if p.bldr.plugin != nil { 130 | p.bldr.plugin.Filter(p.name, ">=", value) 131 | } 132 | return p.bldr 133 | } 134 | 135 | // LessThan filter with value. 136 | func (p *fooBarQueryProperty) LessThan(value interface{}) *fooBarQueryBuilder { 137 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, "<", value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // GreaterThan filter with value. 145 | func (p *fooBarQueryProperty) GreaterThan(value interface{}) *fooBarQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, ">", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // Equal filter with value. 154 | func (p *fooBarQueryProperty) Equal(value interface{}) *fooBarQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, "=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // Asc order. 163 | func (p *fooBarQueryProperty) Asc() *fooBarQueryBuilder { 164 | p.bldr.q = p.bldr.q.Order(p.name) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Asc(p.name) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // Desc order. 172 | func (p *fooBarQueryProperty) Desc() *fooBarQueryBuilder { 173 | p.bldr.q = p.bldr.q.Order("-" + p.name) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Desc(p.name) 176 | } 177 | return p.bldr 178 | } 179 | 180 | // aeDataQueryBuilder build query for aeData. 181 | type aeDataQueryBuilder struct { 182 | q *datastore.Query 183 | plugin qbgutils.Plugin 184 | Foo *aeDataQueryProperty 185 | } 186 | 187 | // aeDataQueryProperty has property information for aeDataQueryBuilder. 188 | type aeDataQueryProperty struct { 189 | bldr *aeDataQueryBuilder 190 | name string 191 | } 192 | 193 | // newAEDataQueryBuilder create new AEDataQueryBuilder. 194 | func newAEDataQueryBuilder() *aeDataQueryBuilder { 195 | return newAEDataQueryBuilderWithKind("aeData") 196 | } 197 | 198 | // newAEDataQueryBuilderWithKind create new AEDataQueryBuilder with specific kind. 199 | func newAEDataQueryBuilderWithKind(kind string) *aeDataQueryBuilder { 200 | q := datastore.NewQuery(kind) 201 | bldr := &aeDataQueryBuilder{q: q} 202 | bldr.Foo = &aeDataQueryProperty{ 203 | bldr: bldr, 204 | name: "Foo", 205 | } 206 | 207 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 208 | bldr.plugin = plugger.Plugin() 209 | bldr.plugin.Init("AEData") 210 | } 211 | 212 | return bldr 213 | } 214 | 215 | // Ancestor sets parent key to ancestor query. 216 | func (bldr *aeDataQueryBuilder) Ancestor(parentKey *datastore.Key) *aeDataQueryBuilder { 217 | bldr.q = bldr.q.Ancestor(parentKey) 218 | if bldr.plugin != nil { 219 | bldr.plugin.Ancestor(parentKey) 220 | } 221 | return bldr 222 | } 223 | 224 | // KeysOnly sets keys only option to query. 225 | func (bldr *aeDataQueryBuilder) KeysOnly() *aeDataQueryBuilder { 226 | bldr.q = bldr.q.KeysOnly() 227 | if bldr.plugin != nil { 228 | bldr.plugin.KeysOnly() 229 | } 230 | return bldr 231 | } 232 | 233 | // Start setup to query. 234 | func (bldr *aeDataQueryBuilder) Start(cur datastore.Cursor) *aeDataQueryBuilder { 235 | bldr.q = bldr.q.Start(cur) 236 | if bldr.plugin != nil { 237 | bldr.plugin.Start(cur) 238 | } 239 | return bldr 240 | } 241 | 242 | // Offset setup to query. 243 | func (bldr *aeDataQueryBuilder) Offset(offset int) *aeDataQueryBuilder { 244 | bldr.q = bldr.q.Offset(offset) 245 | if bldr.plugin != nil { 246 | bldr.plugin.Offset(offset) 247 | } 248 | return bldr 249 | } 250 | 251 | // Limit setup to query. 252 | func (bldr *aeDataQueryBuilder) Limit(limit int) *aeDataQueryBuilder { 253 | bldr.q = bldr.q.Limit(limit) 254 | if bldr.plugin != nil { 255 | bldr.plugin.Limit(limit) 256 | } 257 | return bldr 258 | } 259 | 260 | // Query returns *datastore.Query. 261 | func (bldr *aeDataQueryBuilder) Query() *datastore.Query { 262 | return bldr.q 263 | } 264 | 265 | // Filter with op & value. 266 | func (p *aeDataQueryProperty) Filter(op string, value interface{}) *aeDataQueryBuilder { 267 | switch op { 268 | case "<=": 269 | p.LessThanOrEqual(value) 270 | case ">=": 271 | p.GreaterThanOrEqual(value) 272 | case "<": 273 | p.LessThan(value) 274 | case ">": 275 | p.GreaterThan(value) 276 | case "=": 277 | p.Equal(value) 278 | default: 279 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 280 | } 281 | if p.bldr.plugin != nil { 282 | p.bldr.plugin.Filter(p.name, op, value) 283 | } 284 | return p.bldr 285 | } 286 | 287 | // LessThanOrEqual filter with value. 288 | func (p *aeDataQueryProperty) LessThanOrEqual(value interface{}) *aeDataQueryBuilder { 289 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 290 | if p.bldr.plugin != nil { 291 | p.bldr.plugin.Filter(p.name, "<=", value) 292 | } 293 | return p.bldr 294 | } 295 | 296 | // GreaterThanOrEqual filter with value. 297 | func (p *aeDataQueryProperty) GreaterThanOrEqual(value interface{}) *aeDataQueryBuilder { 298 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 299 | if p.bldr.plugin != nil { 300 | p.bldr.plugin.Filter(p.name, ">=", value) 301 | } 302 | return p.bldr 303 | } 304 | 305 | // LessThan filter with value. 306 | func (p *aeDataQueryProperty) LessThan(value interface{}) *aeDataQueryBuilder { 307 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 308 | if p.bldr.plugin != nil { 309 | p.bldr.plugin.Filter(p.name, "<", value) 310 | } 311 | return p.bldr 312 | } 313 | 314 | // GreaterThan filter with value. 315 | func (p *aeDataQueryProperty) GreaterThan(value interface{}) *aeDataQueryBuilder { 316 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 317 | if p.bldr.plugin != nil { 318 | p.bldr.plugin.Filter(p.name, ">", value) 319 | } 320 | return p.bldr 321 | } 322 | 323 | // Equal filter with value. 324 | func (p *aeDataQueryProperty) Equal(value interface{}) *aeDataQueryBuilder { 325 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 326 | if p.bldr.plugin != nil { 327 | p.bldr.plugin.Filter(p.name, "=", value) 328 | } 329 | return p.bldr 330 | } 331 | 332 | // Asc order. 333 | func (p *aeDataQueryProperty) Asc() *aeDataQueryBuilder { 334 | p.bldr.q = p.bldr.q.Order(p.name) 335 | if p.bldr.plugin != nil { 336 | p.bldr.plugin.Asc(p.name) 337 | } 338 | return p.bldr 339 | } 340 | 341 | // Desc order. 342 | func (p *aeDataQueryProperty) Desc() *aeDataQueryBuilder { 343 | p.bldr.q = p.bldr.q.Order("-" + p.name) 344 | if p.bldr.plugin != nil { 345 | p.bldr.plugin.Desc(p.name) 346 | } 347 | return p.bldr 348 | } 349 | 350 | // iisQueryBuilder build query for iis. 351 | type iisQueryBuilder struct { 352 | q *datastore.Query 353 | plugin qbgutils.Plugin 354 | Foo *iisQueryProperty 355 | } 356 | 357 | // iisQueryProperty has property information for iisQueryBuilder. 358 | type iisQueryProperty struct { 359 | bldr *iisQueryBuilder 360 | name string 361 | } 362 | 363 | // newIISQueryBuilder create new IISQueryBuilder. 364 | func newIISQueryBuilder() *iisQueryBuilder { 365 | return newIISQueryBuilderWithKind("iis") 366 | } 367 | 368 | // newIISQueryBuilderWithKind create new IISQueryBuilder with specific kind. 369 | func newIISQueryBuilderWithKind(kind string) *iisQueryBuilder { 370 | q := datastore.NewQuery(kind) 371 | bldr := &iisQueryBuilder{q: q} 372 | bldr.Foo = &iisQueryProperty{ 373 | bldr: bldr, 374 | name: "Foo", 375 | } 376 | 377 | if plugger, ok := interface{}(bldr).(qbgutils.Plugger); ok { 378 | bldr.plugin = plugger.Plugin() 379 | bldr.plugin.Init("IIS") 380 | } 381 | 382 | return bldr 383 | } 384 | 385 | // Ancestor sets parent key to ancestor query. 386 | func (bldr *iisQueryBuilder) Ancestor(parentKey *datastore.Key) *iisQueryBuilder { 387 | bldr.q = bldr.q.Ancestor(parentKey) 388 | if bldr.plugin != nil { 389 | bldr.plugin.Ancestor(parentKey) 390 | } 391 | return bldr 392 | } 393 | 394 | // KeysOnly sets keys only option to query. 395 | func (bldr *iisQueryBuilder) KeysOnly() *iisQueryBuilder { 396 | bldr.q = bldr.q.KeysOnly() 397 | if bldr.plugin != nil { 398 | bldr.plugin.KeysOnly() 399 | } 400 | return bldr 401 | } 402 | 403 | // Start setup to query. 404 | func (bldr *iisQueryBuilder) Start(cur datastore.Cursor) *iisQueryBuilder { 405 | bldr.q = bldr.q.Start(cur) 406 | if bldr.plugin != nil { 407 | bldr.plugin.Start(cur) 408 | } 409 | return bldr 410 | } 411 | 412 | // Offset setup to query. 413 | func (bldr *iisQueryBuilder) Offset(offset int) *iisQueryBuilder { 414 | bldr.q = bldr.q.Offset(offset) 415 | if bldr.plugin != nil { 416 | bldr.plugin.Offset(offset) 417 | } 418 | return bldr 419 | } 420 | 421 | // Limit setup to query. 422 | func (bldr *iisQueryBuilder) Limit(limit int) *iisQueryBuilder { 423 | bldr.q = bldr.q.Limit(limit) 424 | if bldr.plugin != nil { 425 | bldr.plugin.Limit(limit) 426 | } 427 | return bldr 428 | } 429 | 430 | // Query returns *datastore.Query. 431 | func (bldr *iisQueryBuilder) Query() *datastore.Query { 432 | return bldr.q 433 | } 434 | 435 | // Filter with op & value. 436 | func (p *iisQueryProperty) Filter(op string, value interface{}) *iisQueryBuilder { 437 | switch op { 438 | case "<=": 439 | p.LessThanOrEqual(value) 440 | case ">=": 441 | p.GreaterThanOrEqual(value) 442 | case "<": 443 | p.LessThan(value) 444 | case ">": 445 | p.GreaterThan(value) 446 | case "=": 447 | p.Equal(value) 448 | default: 449 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 450 | } 451 | if p.bldr.plugin != nil { 452 | p.bldr.plugin.Filter(p.name, op, value) 453 | } 454 | return p.bldr 455 | } 456 | 457 | // LessThanOrEqual filter with value. 458 | func (p *iisQueryProperty) LessThanOrEqual(value interface{}) *iisQueryBuilder { 459 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 460 | if p.bldr.plugin != nil { 461 | p.bldr.plugin.Filter(p.name, "<=", value) 462 | } 463 | return p.bldr 464 | } 465 | 466 | // GreaterThanOrEqual filter with value. 467 | func (p *iisQueryProperty) GreaterThanOrEqual(value interface{}) *iisQueryBuilder { 468 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 469 | if p.bldr.plugin != nil { 470 | p.bldr.plugin.Filter(p.name, ">=", value) 471 | } 472 | return p.bldr 473 | } 474 | 475 | // LessThan filter with value. 476 | func (p *iisQueryProperty) LessThan(value interface{}) *iisQueryBuilder { 477 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 478 | if p.bldr.plugin != nil { 479 | p.bldr.plugin.Filter(p.name, "<", value) 480 | } 481 | return p.bldr 482 | } 483 | 484 | // GreaterThan filter with value. 485 | func (p *iisQueryProperty) GreaterThan(value interface{}) *iisQueryBuilder { 486 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 487 | if p.bldr.plugin != nil { 488 | p.bldr.plugin.Filter(p.name, ">", value) 489 | } 490 | return p.bldr 491 | } 492 | 493 | // Equal filter with value. 494 | func (p *iisQueryProperty) Equal(value interface{}) *iisQueryBuilder { 495 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 496 | if p.bldr.plugin != nil { 497 | p.bldr.plugin.Filter(p.name, "=", value) 498 | } 499 | return p.bldr 500 | } 501 | 502 | // Asc order. 503 | func (p *iisQueryProperty) Asc() *iisQueryBuilder { 504 | p.bldr.q = p.bldr.q.Order(p.name) 505 | if p.bldr.plugin != nil { 506 | p.bldr.plugin.Asc(p.name) 507 | } 508 | return p.bldr 509 | } 510 | 511 | // Desc order. 512 | func (p *iisQueryProperty) Desc() *iisQueryBuilder { 513 | p.bldr.q = p.bldr.q.Order("-" + p.name) 514 | if p.bldr.plugin != nil { 515 | p.bldr.plugin.Desc(p.name) 516 | } 517 | return p.bldr 518 | } 519 | -------------------------------------------------------------------------------- /v2/misc/fixture/h/model.go: -------------------------------------------------------------------------------- 1 | //go:generate qbg -output model_query.go -inlineinterfaces . 2 | package h 3 | 4 | // +qbg 5 | type Sample struct { 6 | Kind string `goon:"kind,sample_kind"` 7 | Foo string 8 | } 9 | -------------------------------------------------------------------------------- /v2/misc/fixture/h/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -output misc/fixture/h/model_query.go -inlineinterfaces misc/fixture/h; DO NOT EDIT 2 | 3 | package h 4 | 5 | import ( 6 | "google.golang.org/appengine/v2/datastore" 7 | ) 8 | 9 | // Plugin supply hook point for query constructions. 10 | type Plugin interface { 11 | Init(typeName string) 12 | Ancestor(ancestor *datastore.Key) 13 | KeysOnly() 14 | Start(cur datastore.Cursor) 15 | Offset(offset int) 16 | Limit(limit int) 17 | Filter(name, op string, value interface{}) 18 | Asc(name string) 19 | Desc(name string) 20 | } 21 | 22 | // Plugger supply Plugin component. 23 | type Plugger interface { 24 | Plugin() Plugin 25 | } 26 | 27 | // SampleQueryBuilder build query for Sample. 28 | type SampleQueryBuilder struct { 29 | q *datastore.Query 30 | plugin Plugin 31 | Kind *SampleQueryProperty 32 | Foo *SampleQueryProperty 33 | } 34 | 35 | // SampleQueryProperty has property information for SampleQueryBuilder. 36 | type SampleQueryProperty struct { 37 | bldr *SampleQueryBuilder 38 | name string 39 | } 40 | 41 | // NewSampleQueryBuilder create new SampleQueryBuilder. 42 | func NewSampleQueryBuilder() *SampleQueryBuilder { 43 | return NewSampleQueryBuilderWithKind("sample_kind") 44 | } 45 | 46 | // NewSampleQueryBuilderWithKind create new SampleQueryBuilder with specific kind. 47 | func NewSampleQueryBuilderWithKind(kind string) *SampleQueryBuilder { 48 | q := datastore.NewQuery(kind) 49 | bldr := &SampleQueryBuilder{q: q} 50 | bldr.Kind = &SampleQueryProperty{ 51 | bldr: bldr, 52 | name: "Kind", 53 | } 54 | bldr.Foo = &SampleQueryProperty{ 55 | bldr: bldr, 56 | name: "Foo", 57 | } 58 | 59 | if plugger, ok := interface{}(bldr).(Plugger); ok { 60 | bldr.plugin = plugger.Plugin() 61 | bldr.plugin.Init("Sample") 62 | } 63 | 64 | return bldr 65 | } 66 | 67 | // Ancestor sets parent key to ancestor query. 68 | func (bldr *SampleQueryBuilder) Ancestor(parentKey *datastore.Key) *SampleQueryBuilder { 69 | bldr.q = bldr.q.Ancestor(parentKey) 70 | if bldr.plugin != nil { 71 | bldr.plugin.Ancestor(parentKey) 72 | } 73 | return bldr 74 | } 75 | 76 | // KeysOnly sets keys only option to query. 77 | func (bldr *SampleQueryBuilder) KeysOnly() *SampleQueryBuilder { 78 | bldr.q = bldr.q.KeysOnly() 79 | if bldr.plugin != nil { 80 | bldr.plugin.KeysOnly() 81 | } 82 | return bldr 83 | } 84 | 85 | // Start setup to query. 86 | func (bldr *SampleQueryBuilder) Start(cur datastore.Cursor) *SampleQueryBuilder { 87 | bldr.q = bldr.q.Start(cur) 88 | if bldr.plugin != nil { 89 | bldr.plugin.Start(cur) 90 | } 91 | return bldr 92 | } 93 | 94 | // Offset setup to query. 95 | func (bldr *SampleQueryBuilder) Offset(offset int) *SampleQueryBuilder { 96 | bldr.q = bldr.q.Offset(offset) 97 | if bldr.plugin != nil { 98 | bldr.plugin.Offset(offset) 99 | } 100 | return bldr 101 | } 102 | 103 | // Limit setup to query. 104 | func (bldr *SampleQueryBuilder) Limit(limit int) *SampleQueryBuilder { 105 | bldr.q = bldr.q.Limit(limit) 106 | if bldr.plugin != nil { 107 | bldr.plugin.Limit(limit) 108 | } 109 | return bldr 110 | } 111 | 112 | // Query returns *datastore.Query. 113 | func (bldr *SampleQueryBuilder) Query() *datastore.Query { 114 | return bldr.q 115 | } 116 | 117 | // Filter with op & value. 118 | func (p *SampleQueryProperty) Filter(op string, value interface{}) *SampleQueryBuilder { 119 | switch op { 120 | case "<=": 121 | p.LessThanOrEqual(value) 122 | case ">=": 123 | p.GreaterThanOrEqual(value) 124 | case "<": 125 | p.LessThan(value) 126 | case ">": 127 | p.GreaterThan(value) 128 | case "=": 129 | p.Equal(value) 130 | default: 131 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 132 | } 133 | if p.bldr.plugin != nil { 134 | p.bldr.plugin.Filter(p.name, op, value) 135 | } 136 | return p.bldr 137 | } 138 | 139 | // LessThanOrEqual filter with value. 140 | func (p *SampleQueryProperty) LessThanOrEqual(value interface{}) *SampleQueryBuilder { 141 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 142 | if p.bldr.plugin != nil { 143 | p.bldr.plugin.Filter(p.name, "<=", value) 144 | } 145 | return p.bldr 146 | } 147 | 148 | // GreaterThanOrEqual filter with value. 149 | func (p *SampleQueryProperty) GreaterThanOrEqual(value interface{}) *SampleQueryBuilder { 150 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 151 | if p.bldr.plugin != nil { 152 | p.bldr.plugin.Filter(p.name, ">=", value) 153 | } 154 | return p.bldr 155 | } 156 | 157 | // LessThan filter with value. 158 | func (p *SampleQueryProperty) LessThan(value interface{}) *SampleQueryBuilder { 159 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 160 | if p.bldr.plugin != nil { 161 | p.bldr.plugin.Filter(p.name, "<", value) 162 | } 163 | return p.bldr 164 | } 165 | 166 | // GreaterThan filter with value. 167 | func (p *SampleQueryProperty) GreaterThan(value interface{}) *SampleQueryBuilder { 168 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 169 | if p.bldr.plugin != nil { 170 | p.bldr.plugin.Filter(p.name, ">", value) 171 | } 172 | return p.bldr 173 | } 174 | 175 | // Equal filter with value. 176 | func (p *SampleQueryProperty) Equal(value interface{}) *SampleQueryBuilder { 177 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 178 | if p.bldr.plugin != nil { 179 | p.bldr.plugin.Filter(p.name, "=", value) 180 | } 181 | return p.bldr 182 | } 183 | 184 | // Asc order. 185 | func (p *SampleQueryProperty) Asc() *SampleQueryBuilder { 186 | p.bldr.q = p.bldr.q.Order(p.name) 187 | if p.bldr.plugin != nil { 188 | p.bldr.plugin.Asc(p.name) 189 | } 190 | return p.bldr 191 | } 192 | 193 | // Desc order. 194 | func (p *SampleQueryProperty) Desc() *SampleQueryBuilder { 195 | p.bldr.q = p.bldr.q.Order("-" + p.name) 196 | if p.bldr.plugin != nil { 197 | p.bldr.plugin.Desc(p.name) 198 | } 199 | return p.bldr 200 | } 201 | -------------------------------------------------------------------------------- /v2/misc/fixture/i/model.go: -------------------------------------------------------------------------------- 1 | package i 2 | 3 | import ( 4 | "context" 5 | 6 | "go.mercari.io/datastore/v2" 7 | ) 8 | 9 | var _ datastore.PropertyTranslator = UserID(0) 10 | 11 | type contextClient struct{} 12 | 13 | const kindUser = "User" 14 | 15 | type UserID int64 16 | 17 | // +qbg 18 | type User struct { 19 | ID UserID `datastore:"-" boom:"id" json:"id"` 20 | Name string `json:"name"` 21 | MentorID UserID `json:"mentorID"` 22 | } 23 | 24 | func (id UserID) ToPropertyValue(ctx context.Context) (interface{}, error) { 25 | client := ctx.Value(contextClient{}).(datastore.Client) 26 | key := client.IDKey(kindUser, int64(id), nil) 27 | return key, nil 28 | } 29 | 30 | func (id UserID) FromPropertyValue(ctx context.Context, p datastore.Property) (dst interface{}, err error) { 31 | key, ok := p.Value.(datastore.Key) 32 | if !ok { 33 | return nil, datastore.ErrInvalidEntityType 34 | } 35 | return UserID(key.ID()), nil 36 | } 37 | -------------------------------------------------------------------------------- /v2/misc/fixture/i/model_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by qbg -output misc/fixture/i/model_query.go -usedatastorewrapper misc/fixture/i; DO NOT EDIT 2 | 3 | package i 4 | 5 | import ( 6 | "go.mercari.io/datastore/v2" 7 | ) 8 | 9 | // Plugin supply hook point for query constructions. 10 | type Plugin interface { 11 | Init(typeName string) 12 | Ancestor(ancestor datastore.Key) 13 | KeysOnly() 14 | Start(cur datastore.Cursor) 15 | Offset(offset int) 16 | Limit(limit int) 17 | Filter(name, op string, value interface{}) 18 | Asc(name string) 19 | Desc(name string) 20 | } 21 | 22 | // Plugger supply Plugin component. 23 | type Plugger interface { 24 | Plugin() Plugin 25 | } 26 | 27 | // UserQueryBuilder build query for User. 28 | type UserQueryBuilder struct { 29 | q datastore.Query 30 | plugin Plugin 31 | ID *UserQueryProperty 32 | Name *UserQueryProperty 33 | MentorID *UserQueryProperty 34 | } 35 | 36 | // UserQueryProperty has property information for UserQueryBuilder. 37 | type UserQueryProperty struct { 38 | bldr *UserQueryBuilder 39 | name string 40 | } 41 | 42 | // NewUserQueryBuilder create new UserQueryBuilder. 43 | func NewUserQueryBuilder(client datastore.Client) *UserQueryBuilder { 44 | return NewUserQueryBuilderWithKind(client, "User") 45 | } 46 | 47 | // NewUserQueryBuilderWithKind create new UserQueryBuilder with specific kind. 48 | func NewUserQueryBuilderWithKind(client datastore.Client, kind string) *UserQueryBuilder { 49 | q := client.NewQuery(kind) 50 | bldr := &UserQueryBuilder{q: q} 51 | bldr.ID = &UserQueryProperty{ 52 | bldr: bldr, 53 | name: "__key__", 54 | } 55 | bldr.Name = &UserQueryProperty{ 56 | bldr: bldr, 57 | name: "Name", 58 | } 59 | bldr.MentorID = &UserQueryProperty{ 60 | bldr: bldr, 61 | name: "MentorID", 62 | } 63 | 64 | if plugger, ok := interface{}(bldr).(Plugger); ok { 65 | bldr.plugin = plugger.Plugin() 66 | bldr.plugin.Init("User") 67 | } 68 | 69 | return bldr 70 | } 71 | 72 | // Ancestor sets parent key to ancestor query. 73 | func (bldr *UserQueryBuilder) Ancestor(parentKey datastore.Key) *UserQueryBuilder { 74 | bldr.q = bldr.q.Ancestor(parentKey) 75 | if bldr.plugin != nil { 76 | bldr.plugin.Ancestor(parentKey) 77 | } 78 | return bldr 79 | } 80 | 81 | // KeysOnly sets keys only option to query. 82 | func (bldr *UserQueryBuilder) KeysOnly() *UserQueryBuilder { 83 | bldr.q = bldr.q.KeysOnly() 84 | if bldr.plugin != nil { 85 | bldr.plugin.KeysOnly() 86 | } 87 | return bldr 88 | } 89 | 90 | // Start setup to query. 91 | func (bldr *UserQueryBuilder) Start(cur datastore.Cursor) *UserQueryBuilder { 92 | bldr.q = bldr.q.Start(cur) 93 | if bldr.plugin != nil { 94 | bldr.plugin.Start(cur) 95 | } 96 | return bldr 97 | } 98 | 99 | // Offset setup to query. 100 | func (bldr *UserQueryBuilder) Offset(offset int) *UserQueryBuilder { 101 | bldr.q = bldr.q.Offset(offset) 102 | if bldr.plugin != nil { 103 | bldr.plugin.Offset(offset) 104 | } 105 | return bldr 106 | } 107 | 108 | // Limit setup to query. 109 | func (bldr *UserQueryBuilder) Limit(limit int) *UserQueryBuilder { 110 | bldr.q = bldr.q.Limit(limit) 111 | if bldr.plugin != nil { 112 | bldr.plugin.Limit(limit) 113 | } 114 | return bldr 115 | } 116 | 117 | // Query returns *datastore.Query. 118 | func (bldr *UserQueryBuilder) Query() datastore.Query { 119 | return bldr.q 120 | } 121 | 122 | // Filter with op & value. 123 | func (p *UserQueryProperty) Filter(op string, value interface{}) *UserQueryBuilder { 124 | switch op { 125 | case "<=": 126 | p.LessThanOrEqual(value) 127 | case ">=": 128 | p.GreaterThanOrEqual(value) 129 | case "<": 130 | p.LessThan(value) 131 | case ">": 132 | p.GreaterThan(value) 133 | case "=": 134 | p.Equal(value) 135 | default: 136 | p.bldr.q = p.bldr.q.Filter(p.name+" "+op, value) // error raised by native query 137 | } 138 | if p.bldr.plugin != nil { 139 | p.bldr.plugin.Filter(p.name, op, value) 140 | } 141 | return p.bldr 142 | } 143 | 144 | // LessThanOrEqual filter with value. 145 | func (p *UserQueryProperty) LessThanOrEqual(value interface{}) *UserQueryBuilder { 146 | p.bldr.q = p.bldr.q.Filter(p.name+" <=", value) 147 | if p.bldr.plugin != nil { 148 | p.bldr.plugin.Filter(p.name, "<=", value) 149 | } 150 | return p.bldr 151 | } 152 | 153 | // GreaterThanOrEqual filter with value. 154 | func (p *UserQueryProperty) GreaterThanOrEqual(value interface{}) *UserQueryBuilder { 155 | p.bldr.q = p.bldr.q.Filter(p.name+" >=", value) 156 | if p.bldr.plugin != nil { 157 | p.bldr.plugin.Filter(p.name, ">=", value) 158 | } 159 | return p.bldr 160 | } 161 | 162 | // LessThan filter with value. 163 | func (p *UserQueryProperty) LessThan(value interface{}) *UserQueryBuilder { 164 | p.bldr.q = p.bldr.q.Filter(p.name+" <", value) 165 | if p.bldr.plugin != nil { 166 | p.bldr.plugin.Filter(p.name, "<", value) 167 | } 168 | return p.bldr 169 | } 170 | 171 | // GreaterThan filter with value. 172 | func (p *UserQueryProperty) GreaterThan(value interface{}) *UserQueryBuilder { 173 | p.bldr.q = p.bldr.q.Filter(p.name+" >", value) 174 | if p.bldr.plugin != nil { 175 | p.bldr.plugin.Filter(p.name, ">", value) 176 | } 177 | return p.bldr 178 | } 179 | 180 | // Equal filter with value. 181 | func (p *UserQueryProperty) Equal(value interface{}) *UserQueryBuilder { 182 | p.bldr.q = p.bldr.q.Filter(p.name+" =", value) 183 | if p.bldr.plugin != nil { 184 | p.bldr.plugin.Filter(p.name, "=", value) 185 | } 186 | return p.bldr 187 | } 188 | 189 | // Asc order. 190 | func (p *UserQueryProperty) Asc() *UserQueryBuilder { 191 | p.bldr.q = p.bldr.q.Order(p.name) 192 | if p.bldr.plugin != nil { 193 | p.bldr.plugin.Asc(p.name) 194 | } 195 | return p.bldr 196 | } 197 | 198 | // Desc order. 199 | func (p *UserQueryProperty) Desc() *UserQueryBuilder { 200 | p.bldr.q = p.bldr.q.Order("-" + p.name) 201 | if p.bldr.plugin != nil { 202 | p.bldr.plugin.Desc(p.name) 203 | } 204 | return p.bldr 205 | } 206 | -------------------------------------------------------------------------------- /v2/qbgutils/utils.go: -------------------------------------------------------------------------------- 1 | package qbgutils 2 | 3 | import "google.golang.org/appengine/v2/datastore" 4 | 5 | // Plugin supply hook point for query constructions. 6 | type Plugin interface { 7 | Init(typeName string) 8 | Ancestor(ancestor *datastore.Key) 9 | KeysOnly() 10 | Start(cur datastore.Cursor) 11 | Offset(offset int) 12 | Limit(limit int) 13 | Filter(name, op string, value interface{}) 14 | Asc(name string) 15 | Desc(name string) 16 | } 17 | 18 | // Plugger supply Plugin component. 19 | type Plugger interface { 20 | Plugin() Plugin 21 | } 22 | -------------------------------------------------------------------------------- /v2/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | go mod download 4 | 5 | go fmt ./... 6 | go run cmd/qbg/main.go -type Sample -output misc/fixture/a/model_query.go misc/fixture/a 7 | go run cmd/qbg/main.go -type Sample -output misc/fixture/b/model_query.go misc/fixture/b 8 | go run cmd/qbg/main.go -output misc/fixture/c/model_query.go misc/fixture/c 9 | go run cmd/qbg/main.go -output misc/fixture/d/model_query.go misc/fixture/d 10 | go run cmd/qbg/main.go -output misc/fixture/e/model_query.go misc/fixture/e 11 | go run cmd/qbg/main.go -output misc/fixture/f/model_query.go misc/fixture/f 12 | go run cmd/qbg/main.go -output misc/fixture/g/model_query.go -private misc/fixture/g 13 | go run cmd/qbg/main.go -output misc/fixture/h/model_query.go -inlineinterfaces misc/fixture/h 14 | go run cmd/qbg/main.go -output misc/fixture/i/model_query.go -usedatastorewrapper misc/fixture/i 15 | -------------------------------------------------------------------------------- /v2/template.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | const pluginTemplate = ` 4 | // Plugin supply hook point for query constructions. 5 | type Plugin interface { 6 | Init(typeName string) 7 | Ancestor(ancestor {{.DSKeyType}}) 8 | KeysOnly() 9 | Start(cur datastore.Cursor) 10 | Offset(offset int) 11 | Limit(limit int) 12 | Filter(name, op string, value interface{}) 13 | Asc(name string) 14 | Desc(name string) 15 | } 16 | 17 | // Plugger supply Plugin component. 18 | type Plugger interface { 19 | Plugin() Plugin 20 | } 21 | ` 22 | 23 | const structTemplate = ` 24 | {{$st := .}} 25 | // {{.Name}}QueryBuilder build query for {{.Name}}. 26 | type {{.Name}}QueryBuilder struct { 27 | q {{.DSQueryType}} 28 | plugin {{.PluginType}} 29 | {{- range $idx, $f := .Fields}} 30 | {{$f.Tag.PropertyNameAlter}} *{{$st.Name}}QueryProperty 31 | {{- end}} 32 | } 33 | 34 | // {{.Name}}QueryProperty has property information for {{.Name}}QueryBuilder. 35 | type {{.Name}}QueryProperty struct { 36 | bldr *{{.Name}}QueryBuilder 37 | name string 38 | } 39 | 40 | // {{.NewWord}}{{.SimpleName}}QueryBuilder create new {{.SimpleName}}QueryBuilder. 41 | func {{.NewWord}}{{.SimpleName}}QueryBuilder({{if .UserDatastoreWrapper}}client datastore.Client,{{end}}) *{{.Name}}QueryBuilder { 42 | return {{.NewWord}}{{.SimpleName}}QueryBuilderWithKind({{if .UserDatastoreWrapper}}client,{{end}}"{{.Kind}}") 43 | } 44 | 45 | // {{.NewWord}}{{.SimpleName}}QueryBuilderWithKind create new {{.SimpleName}}QueryBuilder with specific kind. 46 | func {{.NewWord}}{{.SimpleName}}QueryBuilderWithKind({{if .UserDatastoreWrapper}}client datastore.Client,{{end}}kind string) *{{.Name}}QueryBuilder { 47 | q := {{.DSNewQuery}}(kind) 48 | bldr := &{{.Name}}QueryBuilder{q:q} 49 | {{- range $idx, $f := .Fields}} 50 | bldr.{{$f.Tag.PropertyNameAlter}}= &{{$st.Name}}QueryProperty{ 51 | bldr : bldr, 52 | name : {{if $f.Tag.ID}} "__key__" {{else}} "{{$f.Tag.Name}}" {{end}}, 53 | } 54 | {{- end}} 55 | 56 | if plugger, ok := interface{}(bldr).({{.PluggerType}}); ok { 57 | bldr.plugin = plugger.Plugin() 58 | bldr.plugin.Init("{{.SimpleName}}") 59 | } 60 | 61 | return bldr 62 | } 63 | 64 | // Ancestor sets parent key to ancestor query. 65 | func (bldr *{{.Name}}QueryBuilder) Ancestor(parentKey {{.DSKeyType}}) *{{.Name}}QueryBuilder { 66 | bldr.q = bldr.q.Ancestor(parentKey) 67 | if bldr.plugin != nil { 68 | bldr.plugin.Ancestor(parentKey) 69 | } 70 | return bldr 71 | } 72 | 73 | // KeysOnly sets keys only option to query. 74 | func (bldr *{{.Name}}QueryBuilder) KeysOnly() *{{.Name}}QueryBuilder { 75 | bldr.q = bldr.q.KeysOnly() 76 | if bldr.plugin != nil { 77 | bldr.plugin.KeysOnly() 78 | } 79 | return bldr 80 | } 81 | 82 | // Start setup to query. 83 | func (bldr *{{.Name}}QueryBuilder) Start(cur datastore.Cursor) *{{.Name}}QueryBuilder { 84 | bldr.q = bldr.q.Start(cur) 85 | if bldr.plugin != nil { 86 | bldr.plugin.Start(cur) 87 | } 88 | return bldr 89 | } 90 | 91 | // Offset setup to query. 92 | func (bldr *{{.Name}}QueryBuilder) Offset(offset int) *{{.Name}}QueryBuilder { 93 | bldr.q = bldr.q.Offset(offset) 94 | if bldr.plugin != nil { 95 | bldr.plugin.Offset(offset) 96 | } 97 | return bldr 98 | } 99 | 100 | // Limit setup to query. 101 | func (bldr *{{.Name}}QueryBuilder) Limit(limit int) *{{.Name}}QueryBuilder { 102 | bldr.q = bldr.q.Limit(limit) 103 | if bldr.plugin != nil { 104 | bldr.plugin.Limit(limit) 105 | } 106 | return bldr 107 | } 108 | 109 | // Query returns *datastore.Query. 110 | func (bldr *{{.Name}}QueryBuilder) Query() {{.DSQueryType}} { 111 | return bldr.q 112 | } 113 | 114 | // Filter with op & value. 115 | func (p *{{.Name}}QueryProperty) Filter(op string, value interface{}) *{{.Name}}QueryBuilder { 116 | switch op { 117 | case "<=": 118 | p.LessThanOrEqual(value) 119 | case ">=": 120 | p.GreaterThanOrEqual(value) 121 | case "<": 122 | p.LessThan(value) 123 | case ">": 124 | p.GreaterThan(value) 125 | case "=": 126 | p.Equal(value) 127 | default: 128 | p.bldr.q = p.bldr.q.Filter(p.name + " " + op, value) // error raised by native query 129 | } 130 | if p.bldr.plugin != nil { 131 | p.bldr.plugin.Filter(p.name, op, value) 132 | } 133 | return p.bldr 134 | } 135 | 136 | // LessThanOrEqual filter with value. 137 | func (p *{{.Name}}QueryProperty) LessThanOrEqual(value interface{}) *{{.Name}}QueryBuilder { 138 | p.bldr.q = p.bldr.q.Filter(p.name + " <=", value) 139 | if p.bldr.plugin != nil { 140 | p.bldr.plugin.Filter(p.name,"<=", value) 141 | } 142 | return p.bldr 143 | } 144 | 145 | // GreaterThanOrEqual filter with value. 146 | func (p *{{.Name}}QueryProperty) GreaterThanOrEqual(value interface{}) *{{.Name}}QueryBuilder { 147 | p.bldr.q = p.bldr.q.Filter(p.name + " >=", value) 148 | if p.bldr.plugin != nil { 149 | p.bldr.plugin.Filter(p.name,">=", value) 150 | } 151 | return p.bldr 152 | } 153 | 154 | // LessThan filter with value. 155 | func (p *{{.Name}}QueryProperty) LessThan(value interface{}) *{{.Name}}QueryBuilder { 156 | p.bldr.q = p.bldr.q.Filter(p.name + " <", value) 157 | if p.bldr.plugin != nil { 158 | p.bldr.plugin.Filter(p.name,"<", value) 159 | } 160 | return p.bldr 161 | } 162 | 163 | // GreaterThan filter with value. 164 | func (p *{{.Name}}QueryProperty) GreaterThan(value interface{}) *{{.Name}}QueryBuilder { 165 | p.bldr.q = p.bldr.q.Filter(p.name + " >", value) 166 | if p.bldr.plugin != nil { 167 | p.bldr.plugin.Filter(p.name,">", value) 168 | } 169 | return p.bldr 170 | } 171 | 172 | // Equal filter with value. 173 | func (p *{{.Name}}QueryProperty) Equal(value interface{}) *{{.Name}}QueryBuilder { 174 | p.bldr.q = p.bldr.q.Filter(p.name + " =", value) 175 | if p.bldr.plugin != nil { 176 | p.bldr.plugin.Filter(p.name,"=", value) 177 | } 178 | return p.bldr 179 | } 180 | 181 | // Asc order. 182 | func (p *{{.Name}}QueryProperty) Asc() *{{.Name}}QueryBuilder { 183 | p.bldr.q = p.bldr.q.Order(p.name) 184 | if p.bldr.plugin != nil { 185 | p.bldr.plugin.Asc(p.name) 186 | } 187 | return p.bldr 188 | } 189 | 190 | // Desc order. 191 | func (p *{{.Name}}QueryProperty) Desc() *{{.Name}}QueryBuilder { 192 | p.bldr.q = p.bldr.q.Order("-" + p.name) 193 | if p.bldr.plugin != nil { 194 | p.bldr.plugin.Desc(p.name) 195 | } 196 | return p.bldr 197 | } 198 | 199 | ` 200 | -------------------------------------------------------------------------------- /v2/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | packages=$(go list ./...) 4 | 5 | go test $packages 6 | -------------------------------------------------------------------------------- /v2/usage_test.go: -------------------------------------------------------------------------------- 1 | package qbg 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/favclip/qbg/v2/misc/fixture/a" 7 | "github.com/favclip/qbg/v2/misc/fixture/e" 8 | "google.golang.org/appengine/v2/aetest" 9 | "google.golang.org/appengine/v2/datastore" 10 | "google.golang.org/appengine/v2/memcache" 11 | ) 12 | 13 | func TestBasicUsage1(t *testing.T) { 14 | c, closer, err := aetest.NewContext() 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | defer closer() 19 | 20 | src := &a.Sample{"Foo!"} 21 | key := datastore.NewIncompleteKey(c, "Sample", nil) 22 | _, err = datastore.Put(c, key, src) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | builder := a.NewSampleQueryBuilder() 28 | builder.Foo.GreaterThan("Foo") 29 | iter := builder.Query().Run(c) 30 | for { 31 | src := &a.Sample{} 32 | key, err = iter.Next(src) 33 | if err == datastore.Done { 34 | break 35 | } else if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | t.Logf("key: %#v, entity: %#v", key, src) 40 | } 41 | } 42 | 43 | func TestBasicUsage2(t *testing.T) { 44 | c, closer, err := aetest.NewContext() 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | defer closer() 49 | 50 | src := &a.Sample{"Foo!"} 51 | key := datastore.NewIncompleteKey(c, "Sample", nil) 52 | _, err = datastore.Put(c, key, src) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | 57 | builder := a.NewSampleQueryBuilder() 58 | builder.Foo.GreaterThan("Foo").KeysOnly().Limit(3) 59 | iter := builder.Query().Run(c) 60 | for { 61 | key, err = iter.Next(nil) 62 | if err == datastore.Done { 63 | break 64 | } else if err != nil { 65 | t.Fatal(err) 66 | } 67 | src := &a.Sample{} 68 | err = datastore.Get(c, key, src) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | 73 | t.Logf("key: %#v, entity: %#v", key, src) 74 | } 75 | } 76 | 77 | func TestPluginUsage(t *testing.T) { 78 | c, closer, err := aetest.NewContext() 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | defer closer() 83 | 84 | parentKey := datastore.NewKey(c, "Test", "T", 0, nil) 85 | 86 | { 87 | // put for Datastore 88 | src := &e.Sample{"Foo!"} 89 | key := datastore.NewIncompleteKey(c, "Sample", parentKey) 90 | _, err = datastore.Put(c, key, src) 91 | if err != nil { 92 | t.Fatal(err) 93 | } 94 | _, err = memcache.Increment(c, "counter", 1, 0) // shift memcache counter 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | } 99 | 100 | builder := e.NewSampleQueryBuilder() 101 | mp, ok := builder.Plugin().(*e.MemcacheQueryPlugin) 102 | if !ok { 103 | t.Fatal("Plugin is not MemcacheQueryPlugin") 104 | } 105 | memcacheCounter, err := memcache.Increment(c, "counter", 0, 0) 106 | if err != nil { 107 | t.Fatal(err) 108 | } 109 | mp.AddCounter(memcacheCounter) 110 | 111 | builder.Ancestor(parentKey).Foo.GreaterThan("Fo").Limit(3) 112 | if str := mp.QueryString(); str != `k=Sample:1:!a=/Test,T:?Foo>"Fo":!l=3` { 113 | t.Fatalf("unexpected: %s", str) 114 | } 115 | 116 | { 117 | // get from Memcache 118 | var list []*e.Sample 119 | _, err = memcache.Gob.Get(c, mp.QueryString(), &list) 120 | if err == memcache.ErrCacheMiss { 121 | // continue 122 | } else if err != nil { 123 | t.Fatal(err) 124 | } else { 125 | t.Log(list) 126 | return 127 | } 128 | } 129 | 130 | { 131 | // get from Datastore 132 | iter := builder.Query().Run(c) 133 | var list []*e.Sample 134 | for { 135 | src := &e.Sample{} 136 | _, err = iter.Next(src) 137 | if err == datastore.Done { 138 | break 139 | } else if err != nil { 140 | t.Fatal(err) 141 | } 142 | list = append(list, src) 143 | } 144 | 145 | // put for Memcache 146 | err = memcache.Gob.Set(c, &memcache.Item{ 147 | Key: mp.QueryString(), 148 | Object: list, 149 | }) 150 | if err != nil { 151 | t.Fatal(err) 152 | } 153 | if len(list) != 1 { 154 | t.Fatalf("unexpected %d", len(list)) 155 | } 156 | } 157 | 158 | { 159 | // get from Memcache 160 | var list []*e.Sample 161 | _, err = memcache.Gob.Get(c, mp.QueryString(), &list) 162 | if err == memcache.ErrCacheMiss { 163 | t.Fatalf("query result is not in memcache") 164 | } else if err != nil { 165 | t.Fatal(err) 166 | } else { 167 | t.Log(list) 168 | } 169 | } 170 | } 171 | --------------------------------------------------------------------------------