github.com/go-generalize/volcago@v1.7.0/generator/templates/gen.go.tmpl (about) 1 // Code generated by volcago. DO NOT EDIT. 2 // generated version: {{ .AppVersion }} 3 package {{ .PackageName }} 4 5 import ( 6 "context" 7 {{- if .MetaFieldsEnabled }} 8 "time" 9 {{- end }} 10 11 "cloud.google.com/go/firestore" 12 {{- if .EnableIndexes }} 13 "github.com/go-utils/xim" 14 {{- end }} 15 "golang.org/x/xerrors" 16 "google.golang.org/api/iterator" 17 "google.golang.org/grpc/codes" 18 "google.golang.org/grpc/status" 19 {{- if ne .ModelImportPath "" }} 20 21 model "{{.ModelImportPath}}" 22 {{- end }} 23 ) 24 {{- $bracketsStart := "{" }} 25 {{- $bracketsEnd := "}" }} 26 {{ $generate := "go:generate" }} 27 //{{ $generate }} {{ .MockGenPath }} -source $GOFILE -destination {{ .MockOutputPath }} 28 29 // {{ .RepositoryInterfaceName }} - Repository of {{ .StructName }} 30 type {{ .RepositoryInterfaceName }} interface { 31 // Single 32 {{ GetFunc }} 33 {{ GetWithDocFunc }} 34 {{ InsertFunc }} 35 {{ UpdateFunc }} 36 {{ StrictUpdateFunc }} 37 {{ DeleteFunc }} 38 {{ DeleteByFunc }} 39 // Multiple 40 {{ GetMultiFunc }} 41 {{ InsertMultiFunc }} 42 {{ UpdateMultiFunc }} 43 {{ DeleteMultiFunc }} 44 {{ DeleteMultiByFunc }} 45 // Single(Transaction) 46 {{ GetWithTxFunc }} 47 {{ GetWithDocWithTxFunc }} 48 {{ InsertWithTxFunc }} 49 {{ UpdateWithTxFunc }} 50 {{ StrictUpdateWithTxFunc }} 51 {{ DeleteWithTxFunc }} 52 {{ DeleteByWithTxFunc }} 53 // Multiple(Transaction) 54 {{ GetMultiWithTxFunc }} 55 {{ InsertMultiWithTxFunc }} 56 {{ UpdateMultiWithTxFunc }} 57 {{ DeleteMultiWithTxFunc }} 58 {{ DeleteMultiByWithTxFunc }} 59 // Search 60 {{ SearchFunc }} 61 {{ SearchWithTxFunc }} 62 {{ SearchByParamFunc }} 63 {{ SearchByParamWithTxFunc }} 64 // misc 65 GetCollection() *firestore.CollectionRef 66 GetCollectionName() string 67 GetDocRef({{ .KeyValueName }} string) *firestore.DocumentRef 68 RunInTransaction() func(ctx context.Context, f func(context.Context, *firestore.Transaction) error, opts ...firestore.TransactionOption) (err error) 69 {{- if eq .IsSubCollection true }} 70 SetParentDoc(doc *firestore.DocumentRef) 71 NewRepositoryByParent(doc *firestore.DocumentRef) {{ .RepositoryInterfaceName }} 72 Free() 73 {{- end }} 74 {{- $structNameRef := .StructNameRef }} 75 {{- if ne ( len .UniqueInfos ) 0 }} 76 // get by unique field 77 {{- $metaFiledsEnabled := .MetaFieldsEnabled }} 78 {{- range $ui := .UniqueInfos }} 79 GetBy{{ $ui.Field }}(ctx context.Context, {{ ToLower $ui.FsTag }} string{{ if $metaFiledsEnabled }}, opts ...GetOption{{ end }}) (*{{ $structNameRef }}, error) 80 GetBy{{ $ui.Field }}WithTx(tx *firestore.Transaction, {{ ToLower $ui.FsTag }} string{{ if $metaFiledsEnabled }}, opts ...GetOption{{ end }}) (*{{ $structNameRef }}, error) 81 {{- end }} 82 {{- end }} 83 } 84 {{ $middlewareName := printf "%sMiddleware" .RepositoryInterfaceName }} 85 // {{ $middlewareName }} - middleware of {{ .RepositoryInterfaceName }} 86 type {{ $middlewareName }} interface { 87 BeforeInsert(ctx context.Context, subject *{{ .StructNameRef }}) (bool, error) 88 BeforeUpdate(ctx context.Context, old, subject *{{ .StructNameRef }}) (bool, error) 89 BeforeDelete(ctx context.Context, subject *{{ .StructNameRef }}, opts ...DeleteOption) (bool, error) 90 BeforeDeleteBy{{ .KeyFieldName }}(ctx context.Context, {{ PluralForm .KeyValueName }} []{{ .KeyFieldType }}, opts ...DeleteOption) (bool, error) 91 } 92 93 type {{ .RepositoryStructName }} struct { 94 collectionName string 95 firestoreClient *firestore.Client 96 {{- if eq .IsSubCollection true }} 97 parentDocument *firestore.DocumentRef 98 collectionGroup *firestore.CollectionGroupRef 99 {{- end }} 100 middleware []{{ $middlewareName }} 101 uniqueRepository *uniqueRepository 102 } 103 104 // New{{ .RepositoryInterfaceName }} - constructor 105 func New{{ .RepositoryInterfaceName }}(firestoreClient *firestore.Client{{ if eq .IsSubCollection true }}, parentDocument *firestore.DocumentRef{{ end }}, middleware ...{{ $middlewareName }}) {{ .RepositoryInterfaceName }} { 106 return &{{ .RepositoryStructName }}{ 107 collectionName: "{{ .CollectionName }}", 108 firestoreClient: firestoreClient,{{ if eq .IsSubCollection true }} 109 parentDocument: parentDocument,{{ end }} 110 middleware: middleware, 111 uniqueRepository: newUniqueRepository(firestoreClient, "{{ .CollectionName }}"), 112 } 113 }{{ if eq .IsSubCollection true }} 114 115 // New{{ .StructName }}CollectionGroupRepository - constructor 116 func New{{ .StructName }}CollectionGroupRepository(firestoreClient *firestore.Client) {{ .RepositoryInterfaceName }} { 117 return &{{ .RepositoryStructName }}{ 118 collectionName: "{{ .CollectionName }}", 119 collectionGroup: firestoreClient.CollectionGroup("{{ .CollectionName }}"), 120 } 121 }{{ end }} 122 {{ if .MetaFieldsEnabled }} 123 func (repo *{{ .RepositoryStructName }}) setMeta(subject *{{ .StructNameRef }}, isInsert bool) { 124 now := time.Now() 125 126 if isInsert { 127 subject.CreatedAt = now 128 } 129 subject.UpdatedAt = now 130 subject.Version++ 131 } 132 133 func (repo *{{ .RepositoryStructName }}) setMetaWithStrictUpdate(param *{{ .StructName }}UpdateParam) { 134 param.UpdatedAt = firestore.ServerTimestamp 135 param.Version = firestore.Increment(1) 136 } 137 {{ end }} 138 func (repo *{{ .RepositoryStructName }}) beforeInsert(ctx context.Context, subject *{{ .StructNameRef }}) error { 139 {{- if .MetaFieldsEnabled }} 140 if subject.Version != 0 { 141 return xerrors.Errorf("insert data must be Version == 0 %+v: %w", subject, ErrVersionConflict) 142 } 143 if subject.DeletedAt != nil { 144 return xerrors.Errorf("insert data must be DeletedAt == nil: %+v", subject) 145 } 146 repo.setMeta(subject, true) 147 {{- end }} 148 repo.uniqueRepository.setMiddleware(ctx) 149 err := repo.uniqueRepository.CheckUnique(ctx, nil, subject) 150 if err != nil { 151 return xerrors.Errorf("unique.middleware error: %w", err) 152 } 153 154 for _, m := range repo.middleware { 155 c, err := m.BeforeInsert(ctx, subject) 156 if err != nil { 157 return xerrors.Errorf("beforeInsert.middleware error: %w", err) 158 } 159 if !c { 160 continue 161 } 162 } 163 164 return nil 165 } 166 167 func (repo *{{ .RepositoryStructName }}) beforeUpdate(ctx context.Context, old, subject *{{ .StructNameRef }}) error { 168 if ctx.Value(transactionInProgressKey{}) != nil && old == nil { 169 var err error 170 doc := repo.GetDocRef(subject.{{ .KeyFieldName }}) 171 old, err = repo.get(context.Background(), doc) 172 if err != nil { 173 if status.Code(err) == codes.NotFound { 174 return ErrNotFound 175 } 176 return xerrors.Errorf("error in Get method: %w", err) 177 } 178 } 179 {{- if .MetaFieldsEnabled }} 180 if old.Version > subject.Version { 181 return xerrors.Errorf( 182 "The data in the database is newer: (db version: %d, target version: %d) %+v: %w", 183 old.Version, subject.Version, subject, ErrVersionConflict, 184 ) 185 } 186 if subject.DeletedAt != nil { 187 return xerrors.Errorf("update data must be DeletedAt == nil: %+v", subject) 188 } 189 repo.setMeta(subject, false) 190 {{- end }} 191 repo.uniqueRepository.setMiddleware(ctx) 192 err := repo.uniqueRepository.CheckUnique(ctx, old, subject) 193 if err != nil { 194 return xerrors.Errorf("unique.middleware error: %w", err) 195 } 196 197 for _, m := range repo.middleware { 198 c, err := m.BeforeUpdate(ctx, old, subject) 199 if err != nil { 200 return xerrors.Errorf("beforeUpdate.middleware error: %w", err) 201 } 202 if !c { 203 continue 204 } 205 } 206 207 return nil 208 } 209 210 func (repo *{{ .RepositoryStructName }}) beforeDelete(ctx context.Context, subject *{{ .StructNameRef }}, opts ...DeleteOption) error { 211 {{- if .MetaFieldsEnabled }} 212 repo.setMeta(subject, false) 213 {{- end }} 214 repo.uniqueRepository.setMiddleware(ctx) 215 err := repo.uniqueRepository.DeleteUnique(ctx, subject) 216 if err != nil { 217 return xerrors.Errorf("unique.middleware error: %w", err) 218 } 219 220 for _, m := range repo.middleware { 221 c, err := m.BeforeDelete(ctx, subject, opts...) 222 if err != nil { 223 return xerrors.Errorf("beforeDelete.middleware error: %w", err) 224 } 225 if !c { 226 continue 227 } 228 } 229 230 return nil 231 } 232 233 // GetCollection - *firestore.CollectionRef getter 234 func (repo *{{ .RepositoryStructName }}) GetCollection() *firestore.CollectionRef { 235 {{- if eq .IsSubCollection true }} 236 if repo.collectionGroup != nil { 237 return nil 238 } 239 {{- end }} 240 return repo.{{ if eq .IsSubCollection true }}parentDocument{{ else }}firestoreClient{{ end }}.Collection(repo.collectionName) 241 } 242 243 // GetCollectionName - CollectionName getter 244 func (repo *{{ .RepositoryStructName }}) GetCollectionName() string { 245 return repo.collectionName 246 } 247 248 // GetDocRef - *firestore.DocumentRef getter 249 func (repo *{{ .RepositoryStructName }}) GetDocRef({{ .KeyValueName }} string) *firestore.DocumentRef { 250 {{- if eq .IsSubCollection true }} 251 if repo.collectionGroup != nil { 252 return nil 253 } 254 {{- end }} 255 return repo.GetCollection().Doc({{ .KeyValueName }}) 256 } 257 258 // RunInTransaction - (*firestore.Client).RunTransaction getter 259 func (repo *{{ .RepositoryStructName }}) RunInTransaction() func(ctx context.Context, f func(context.Context, *firestore.Transaction) error, opts ...firestore.TransactionOption) (err error) { 260 return repo.firestoreClient.RunTransaction 261 } 262 {{- if eq .IsSubCollection true }} 263 264 // SetParentDoc - parent document setter 265 func (repo *{{ .RepositoryStructName }}) SetParentDoc(doc *firestore.DocumentRef) { 266 if doc == nil { 267 return 268 } 269 repo.parentDocument = doc 270 } 271 272 // NewRepositoryByParent - Returns new instance with setting parent document 273 func (repo {{ .RepositoryStructName }}) NewRepositoryByParent(doc *firestore.DocumentRef) {{ .RepositoryInterfaceName }} { 274 if doc == nil { 275 return &repo 276 } 277 repo.parentDocument = doc 278 return &repo 279 } 280 281 // Free - parent document releaser 282 func (repo *{{ .RepositoryStructName }}) Free() { 283 repo.parentDocument = nil 284 } 285 {{- end }} 286 {{ if .EnableIndexes }} 287 func (repo *{{ .RepositoryStructName }}) saveIndexes(subject *{{ .StructNameRef }}) error { 288 idx := xim.NewIndexes(&xim.Config{ 289 IgnoreCase: true, 290 SaveNoFiltersIndex: true, 291 }) 292 {{ $bracketsStart}} 293 {{- range $fi := .FieldInfos }} 294 {{- range $index := $fi.Indexes }} 295 {{- if eq $index.Use true }} 296 idx.{{ if HasSuffix $index.Method "fix" }}{{ PluralForm $index.Method }}{{ else }}{{ $index.Method }}{{ end }}({{ $index.ConstName }}, {{ if eq $index.Method "AddSomething" }}subject.{{ $fi.Field }}{{ else }}string(subject.{{ $fi.Field }}){{ end }}) 297 {{- end }} 298 {{- end }} 299 {{- end }} 300 {{ $bracketsEnd}} 301 indexes, err := idx.Build() 302 if err != nil { 303 return xerrors.Errorf("failed to index build: %w", err) 304 } else if len(indexes) == 0 { 305 return nil 306 } 307 308 subject.Indexes = indexes 309 310 return nil 311 } 312 {{ end }} 313 // {{ .StructName }}SearchParam - params for search 314 type {{ .StructName }}SearchParam struct { 315 {{ GenerateSearchParam .FieldInfos }} 316 {{- if .MetaFieldsEnabled }} 317 318 IncludeSoftDeleted bool 319 CursorKey string 320 CursorLimit int 321 {{- else }} 322 323 CursorKey string 324 CursorLimit int 325 {{- end }} 326 } 327 328 // {{ .StructName }}UpdateParam - params for strict updates 329 type {{ .StructName }}UpdateParam struct { 330 {{ GenerateUpdateParam .FieldInfos }} 331 } 332 333 // Search - search documents 334 // The third argument is firestore.Query, basically you can pass nil 335 func (repo *{{ .RepositoryStructName }}) {{ SearchFunc }} { 336 return repo.search(ctx, param, q) 337 } 338 339 // SearchByParam - search documents by search param 340 func (repo *{{ .RepositoryStructName }}) {{ SearchByParamFunc }} { 341 return repo.searchByParam(ctx, param) 342 } 343 344 // Get - get `{{ .StructName }}` by `{{ .StructName }}.{{ .KeyFieldName }}` 345 func (repo *{{ .RepositoryStructName }}) {{ GetFunc }} { 346 {{- if eq .IsSubCollection true }} 347 if repo.collectionGroup != nil { 348 return nil, ErrNotAvailableCG 349 } 350 {{- end }} 351 doc := repo.GetDocRef({{ .KeyValueName }}) 352 return repo.get(ctx, doc, opts...) 353 } 354 355 // GetWithDoc - get `{{ .StructName }}` by *firestore.DocumentRef 356 func (repo *{{ .RepositoryStructName }}) {{ GetWithDocFunc }} { 357 {{- if eq .IsSubCollection true }} 358 if repo.collectionGroup != nil { 359 return nil, ErrNotAvailableCG 360 } 361 {{- end }} 362 return repo.get(ctx, doc, opts...) 363 } 364 365 // Insert - insert of `{{ .StructName }}` 366 func (repo *{{ .RepositoryStructName }}) {{ InsertFunc }} { 367 {{- if eq .IsSubCollection true }} 368 if repo.collectionGroup != nil { 369 return "", ErrNotAvailableCG 370 } 371 {{- end }} 372 if err := repo.beforeInsert(ctx, subject); err != nil { 373 return "", xerrors.Errorf("before insert error: %w", err) 374 } 375 {{ if eq .EnableIndexes true}} 376 if err = repo.saveIndexes(subject); err != nil { 377 return "", xerrors.Errorf("failed to saveIndexes: %w", err) 378 } 379 {{ end }} 380 return repo.insert(ctx, subject) 381 } 382 383 // Update - update of `{{ .StructName }}` 384 func (repo *{{ .RepositoryStructName }}) {{ UpdateFunc }} { 385 {{- if eq .IsSubCollection true }} 386 if repo.collectionGroup != nil { 387 return ErrNotAvailableCG 388 } 389 {{- end }} 390 doc := repo.GetDocRef(subject.{{ .KeyFieldName }}) 391 392 old, err := repo.get(ctx, doc) 393 if err != nil { 394 if status.Code(err) == codes.NotFound { 395 return ErrNotFound 396 } 397 return xerrors.Errorf("error in Get method: %w", err) 398 } 399 400 if err := repo.beforeUpdate(ctx, old, subject); err != nil { 401 return xerrors.Errorf("before update error: %w", err) 402 } 403 {{ if eq .EnableIndexes true}} 404 if err := repo.saveIndexes(subject); err != nil { 405 return xerrors.Errorf("failed to saveIndexes: %w", err) 406 } 407 {{ end }} 408 return repo.update(ctx, subject) 409 } 410 411 // StrictUpdate - strict update of `{{ .StructName }}` 412 func (repo *{{ .RepositoryStructName }}) {{ StrictUpdateFunc }} { 413 {{- if eq .IsSubCollection true }} 414 if repo.collectionGroup != nil { 415 return ErrNotAvailableCG 416 } 417 {{- end }} 418 return repo.strictUpdate(ctx, id, param, opts...) 419 } 420 421 // Delete - delete of `{{ .StructName }}` 422 func (repo *{{ .RepositoryStructName }}) {{ DeleteFunc }} { 423 {{- if eq .IsSubCollection true }} 424 if repo.collectionGroup != nil { 425 return ErrNotAvailableCG 426 } 427 {{- end }} 428 if err := repo.beforeDelete(ctx, subject, opts...); err != nil { 429 return xerrors.Errorf("before delete error: %w", err) 430 } 431 {{- if .MetaFieldsEnabled }} 432 433 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 434 t := time.Now() 435 subject.DeletedAt = &t 436 if err := repo.update(ctx, subject); err != nil { 437 return xerrors.Errorf("error in update method: %w", err) 438 } 439 return nil 440 } 441 {{- end }} 442 443 return repo.deleteBy{{ .KeyFieldName }}(ctx, subject.{{ .KeyFieldName }}) 444 } 445 446 // DeleteBy{{ .KeyFieldName }} - delete `{{ .StructName }}` by `{{ .StructName }}.{{ .KeyFieldName }}` 447 func (repo *{{ .RepositoryStructName }}) {{ DeleteByFunc }} { 448 {{- if eq .IsSubCollection true }} 449 if repo.collectionGroup != nil { 450 return ErrNotAvailableCG 451 } 452 {{- end }} 453 subject, err := repo.Get(ctx, {{ .KeyValueName }}) 454 if err != nil { 455 return xerrors.Errorf("error in Get method: %w", err) 456 } 457 458 if err := repo.beforeDelete(ctx, subject, opts...); err != nil { 459 return xerrors.Errorf("before delete error: %w", err) 460 } 461 {{ if .MetaFieldsEnabled }} 462 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 463 t := time.Now() 464 subject.DeletedAt = &t 465 if err := repo.update(ctx, subject); err != nil { 466 return xerrors.Errorf("error in update method: %w", err) 467 } 468 return nil 469 } 470 {{ end }} 471 return repo.Delete(ctx, subject, opts...) 472 } 473 474 // GetMulti - get `{{ .StructName }}` in bulk by array of `{{ .StructName }}.{{ .KeyFieldName }}` 475 func (repo *{{ .RepositoryStructName }}) {{ GetMultiFunc }} { 476 {{- if eq .IsSubCollection true }} 477 if repo.collectionGroup != nil { 478 return nil, ErrNotAvailableCG 479 } 480 {{- end }} 481 return repo.getMulti(ctx, {{ PluralForm .KeyValueName }}, opts...) 482 } 483 484 // InsertMulti - bulk insert of `{{ .StructName }}` 485 func (repo *{{ .RepositoryStructName }}) {{ InsertMultiFunc }} { 486 {{- if eq .IsSubCollection true }} 487 if repo.collectionGroup != nil { 488 return nil, ErrNotAvailableCG 489 } 490 {{- end }} 491 492 {{ PluralForm .KeyValueName }} := make([]{{ .KeyFieldType }}, 0, len(subjects)) 493 batches := make([]*firestore.WriteBatch, 0) 494 batch := repo.firestoreClient.Batch() 495 collect := repo.GetCollection() 496 497 for i, subject := range subjects { 498 var ref *firestore.DocumentRef 499 if subject.{{ .KeyFieldName }} == "" { 500 ref = collect.NewDoc() 501 subject.{{ .KeyFieldName }} = ref.ID 502 } else { 503 ref = collect.Doc(subject.{{ .KeyFieldName }}) 504 if s, err := ref.Get(ctx); err == nil { 505 return nil, xerrors.Errorf("already exists [%v]: %#v", subject.{{ .KeyFieldName }}, s) 506 } 507 } 508 509 if err := repo.beforeInsert(ctx, subject); err != nil { 510 return nil, xerrors.Errorf("before insert error(%d) [%v]: %w", i, subject.{{ .KeyFieldName }}, err) 511 } 512 {{ if eq .EnableIndexes true}} 513 if err := repo.saveIndexes(subjects[i]); err != nil { 514 return nil, xerrors.Errorf("failed to saveIndexes: %w", err) 515 } 516 {{ end }} 517 batch.Set(ref, subject) 518 {{ PluralForm .KeyValueName }} = append({{ PluralForm .KeyValueName }}, ref.ID) 519 i++ 520 if (i%500) == 0 && len(subjects) != i { 521 batches = append(batches, batch) 522 batch = repo.firestoreClient.Batch() 523 } 524 } 525 batches = append(batches, batch) 526 527 for _, b := range batches { 528 if _, err := b.Commit(ctx); err != nil { 529 return nil, xerrors.Errorf("error in Commit method: %w", err) 530 } 531 } 532 533 return {{ PluralForm .KeyValueName }}, nil 534 } 535 536 // UpdateMulti - bulk update of `{{ .StructName }}` 537 func (repo *{{ .RepositoryStructName }}) {{ UpdateMultiFunc }} { 538 {{- if eq .IsSubCollection true }} 539 if repo.collectionGroup != nil { 540 return ErrNotAvailableCG 541 } 542 {{- end }} 543 544 batches := make([]*firestore.WriteBatch, 0) 545 batch := repo.firestoreClient.Batch() 546 collect := repo.GetCollection() 547 548 for i, subject := range subjects { 549 ref := collect.Doc(subject.{{ .KeyFieldName }}) 550 snapShot, err := ref.Get(ctx) 551 if err != nil { 552 if status.Code(err) == codes.NotFound { 553 return xerrors.Errorf("not found [%v]: %w", subject.{{ .KeyFieldName }}, err) 554 } 555 return xerrors.Errorf("error in Get method [%v]: %w", subject.{{ .KeyFieldName }}, err) 556 } 557 558 old := new({{ .StructNameRef }}) 559 if err = snapShot.DataTo(&old); err != nil { 560 return xerrors.Errorf("error in DataTo method: %w", err) 561 } 562 563 if err := repo.beforeUpdate(ctx, old, subject); err != nil { 564 return xerrors.Errorf("before update error(%d) [%v]: %w", i, subject.{{ .KeyFieldName }}, err) 565 } 566 {{ if eq .EnableIndexes true}} 567 if err := repo.saveIndexes(subjects[i]); err != nil { 568 return xerrors.Errorf("failed to saveIndexes: %w", err) 569 } 570 {{ end }} 571 batch.Set(ref, subject) 572 i++ 573 if (i%500) == 0 && len(subjects) != i { 574 batches = append(batches, batch) 575 batch = repo.firestoreClient.Batch() 576 } 577 } 578 batches = append(batches, batch) 579 580 for _, b := range batches { 581 if _, err := b.Commit(ctx); err != nil { 582 return xerrors.Errorf("error in Commit method: %w", err) 583 } 584 } 585 586 return nil 587 } 588 589 // DeleteMulti - bulk delete of `{{ .StructName }}` 590 func (repo *{{ .RepositoryStructName }}) {{ DeleteMultiFunc }} { 591 {{- if eq .IsSubCollection true }} 592 if repo.collectionGroup != nil { 593 return ErrNotAvailableCG 594 } 595 {{- end }} 596 597 batches := make([]*firestore.WriteBatch, 0) 598 batch := repo.firestoreClient.Batch() 599 collect := repo.GetCollection() 600 601 for i, subject := range subjects { 602 ref := collect.Doc(subject.{{ .KeyFieldName }}) 603 if _, err := ref.Get(ctx); err != nil { 604 if status.Code(err) == codes.NotFound { 605 return xerrors.Errorf("not found [%v]: %w", subject.{{ .KeyFieldName }}, err) 606 } 607 return xerrors.Errorf("error in Get method [%v]: %w", subject.{{ .KeyFieldName }}, err) 608 } 609 610 if err := repo.beforeDelete(ctx, subject, opts...); err != nil { 611 return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subject.{{ .KeyFieldName }}, err) 612 } 613 {{ if .MetaFieldsEnabled }} 614 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 615 t := time.Now() 616 subject.DeletedAt = &t 617 batch.Set(ref, subject) 618 } else { 619 batch.Delete(ref) 620 } 621 {{ else }} 622 batch.Delete(ref) 623 {{ end }} 624 i++ 625 if (i%500) == 0 && len(subjects) != i { 626 batches = append(batches, batch) 627 batch = repo.firestoreClient.Batch() 628 } 629 } 630 batches = append(batches, batch) 631 632 for _, b := range batches { 633 if _, err := b.Commit(ctx); err != nil { 634 return xerrors.Errorf("error in Commit method: %w", err) 635 } 636 } 637 638 return nil 639 } 640 641 // DeleteMultiBy{{ PluralForm .KeyFieldName }} - delete `{{ .StructName }}` in bulk by array of `{{ .StructName }}.{{ .KeyFieldName }}` 642 func (repo *{{ .RepositoryStructName }}) {{ DeleteMultiByFunc }} { 643 {{- if eq .IsSubCollection true }} 644 if repo.collectionGroup != nil { 645 return ErrNotAvailableCG 646 } 647 {{- end }} 648 subjects := make([]*{{ .StructNameRef }}, len({{ PluralForm .KeyValueName }})) 649 650 opt := GetOption{} 651 if len(opts) > 0 { 652 opt.IncludeSoftDeleted = opts[0].Mode == DeleteModeHard 653 } 654 for i, {{ .KeyValueName }} := range {{ PluralForm .KeyValueName }} { 655 subject, err := repo.Get(ctx, {{ .KeyValueName }}, opt) 656 if err != nil { 657 return xerrors.Errorf("error in Get method: %w", err) 658 } 659 subjects[i] = subject 660 } 661 662 return repo.DeleteMulti(ctx, subjects, opts...) 663 } 664 // SearchWithTx - search documents in transaction 665 func (repo *{{ .RepositoryStructName }}) {{ SearchWithTxFunc }} { 666 return repo.search(tx, param, q) 667 } 668 669 // SearchByParamWithTx - search documents by search param in transaction 670 func (repo *{{ .RepositoryStructName }}) {{ SearchByParamWithTxFunc }} { 671 return repo.searchByParam(tx, param) 672 } 673 674 // GetWithTx - get `{{ .StructName }}` by `{{ .StructName }}.{{ .KeyFieldName }}` in transaction 675 func (repo *{{ .RepositoryStructName }}) {{ GetWithTxFunc }} { 676 {{- if eq .IsSubCollection true }} 677 if repo.collectionGroup != nil { 678 return nil, ErrNotAvailableCG 679 } 680 {{- end }} 681 doc := repo.GetDocRef({{ .KeyValueName }}) 682 return repo.get(tx, doc, opts...) 683 } 684 685 // GetWithDocWithTx - get `{{ .StructName }}` by *firestore.DocumentRef in transaction 686 func (repo *{{ .RepositoryStructName }}) {{ GetWithDocWithTxFunc }} { 687 {{- if eq .IsSubCollection true }} 688 if repo.collectionGroup != nil { 689 return nil, ErrNotAvailableCG 690 } 691 {{- end }} 692 return repo.get(tx, doc, opts...) 693 } 694 695 // InsertWithTx - insert of `{{ .StructName }}` in transaction 696 func (repo *{{ .RepositoryStructName }}) {{ InsertWithTxFunc }} { 697 {{- if eq .IsSubCollection true }} 698 if repo.collectionGroup != nil { 699 return "", ErrNotAvailableCG 700 } 701 {{- end }} 702 if err := repo.beforeInsert(context.WithValue(ctx, transactionInProgressKey{}, tx), subject); err != nil { 703 return "", xerrors.Errorf("before insert error: %w", err) 704 } 705 {{ if eq .EnableIndexes true}} 706 if err := repo.saveIndexes(subject); err != nil { 707 return "", xerrors.Errorf("failed to saveIndexes: %w", err) 708 } 709 {{ end }} 710 return repo.insert(tx, subject) 711 } 712 713 // UpdateWithTx - update of `{{ .StructName }}` in transaction 714 func (repo *{{ .RepositoryStructName }}) {{ UpdateWithTxFunc }} { 715 {{- if eq .IsSubCollection true }} 716 if repo.collectionGroup != nil { 717 return ErrNotAvailableCG 718 } 719 {{- end }} 720 if err := repo.beforeUpdate(context.WithValue(ctx, transactionInProgressKey{}, tx), nil, subject); err != nil { 721 return xerrors.Errorf("before update error: %w", err) 722 } 723 {{ if eq .EnableIndexes true}} 724 if err := repo.saveIndexes(subject); err != nil { 725 return xerrors.Errorf("failed to saveIndexes: %w", err) 726 } 727 {{ end }} 728 return repo.update(tx, subject) 729 } 730 731 // StrictUpdateWithTx - strict update of `{{ .StructName }}` in transaction 732 func (repo *{{ .RepositoryStructName }}) {{ StrictUpdateWithTxFunc }} { 733 {{- if eq .IsSubCollection true }} 734 if repo.collectionGroup != nil { 735 return ErrNotAvailableCG 736 } 737 {{- end }} 738 return repo.strictUpdate(tx, id, param, opts...) 739 } 740 741 // DeleteWithTx - delete of `{{ .StructName }}` in transaction 742 func (repo *{{ .RepositoryStructName }}) {{ DeleteWithTxFunc }} { 743 {{- if eq .IsSubCollection true }} 744 if repo.collectionGroup != nil { 745 return ErrNotAvailableCG 746 } 747 {{- end }} 748 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil { 749 return xerrors.Errorf("before delete error: %w", err) 750 } 751 {{- if .MetaFieldsEnabled }} 752 753 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 754 t := time.Now() 755 subject.DeletedAt = &t 756 if err := repo.update(tx, subject); err != nil { 757 return xerrors.Errorf("error in update method: %w", err) 758 } 759 return nil 760 } 761 {{- end }} 762 763 return repo.deleteBy{{ .KeyFieldName }}(tx, subject.{{ .KeyFieldName }}) 764 } 765 766 // DeleteBy{{ .KeyFieldName }}WithTx - delete `{{ .StructName }}` by `{{ .StructName }}.{{ .KeyFieldName }}` in transaction 767 func (repo *{{ .RepositoryStructName }}) {{ DeleteByWithTxFunc }} { 768 {{- if eq .IsSubCollection true }} 769 if repo.collectionGroup != nil { 770 return ErrNotAvailableCG 771 } 772 {{- end }} 773 subject, err := repo.Get(context.Background(), {{ .KeyValueName }}) 774 if err != nil { 775 return xerrors.Errorf("error in Get method: %w", err) 776 } 777 {{ if .MetaFieldsEnabled }} 778 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 779 t := time.Now() 780 subject.DeletedAt = &t 781 if err := repo.update(tx, subject); err != nil { 782 return xerrors.Errorf("error in update method: %w", err) 783 } 784 return nil 785 } 786 {{ end }} 787 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil { 788 return xerrors.Errorf("before delete error: %w", err) 789 } 790 791 return repo.deleteBy{{ .KeyFieldName }}(tx, {{ .KeyValueName }}) 792 } 793 794 // GetMultiWithTx - get `{{ .StructName }}` in bulk by array of `{{ .StructName }}.{{ .KeyFieldName }}` in transaction 795 func (repo *{{ .RepositoryStructName }}) {{ GetMultiWithTxFunc }} { 796 {{- if eq .IsSubCollection true }} 797 if repo.collectionGroup != nil { 798 return nil, ErrNotAvailableCG 799 } 800 {{- end }} 801 return repo.getMulti(tx, {{ PluralForm .KeyValueName }}, opts...) 802 } 803 804 // InsertMultiWithTx - bulk insert of `{{ .StructName }}` in transaction 805 func (repo *{{ .RepositoryStructName }}) {{ InsertMultiWithTxFunc }} { 806 {{- if eq .IsSubCollection true }} 807 if repo.collectionGroup != nil { 808 return nil, ErrNotAvailableCG 809 } 810 {{- end }} 811 {{ if not .AutomaticGeneration }} 812 for i := range subjects { 813 if _, err := tx.Get(new(firestore.DocumentRef)); err == nil { 814 return nil, xerrors.Errorf("already exists(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 815 } 816 } 817 {{ end }} 818 {{ PluralForm .KeyValueName }} := make([]string, len(subjects)) 819 820 for i := range subjects { 821 if err := repo.beforeInsert(ctx, subjects[i]); err != nil { 822 return nil, xerrors.Errorf("before insert error(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 823 } 824 {{ if eq .EnableIndexes true}} 825 if err := repo.saveIndexes(subjects[i]); err != nil { 826 return nil, xerrors.Errorf("failed to saveIndexes: %w", err) 827 } 828 {{ end }} 829 {{ .KeyValueName }}, err := repo.insert(tx, subjects[i]) 830 if err != nil { 831 return nil, xerrors.Errorf("error in insert method(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 832 } 833 {{ PluralForm .KeyValueName }}[i] = {{ .KeyValueName }} 834 } 835 836 return {{ PluralForm .KeyValueName }}, nil 837 } 838 839 // UpdateMultiWithTx - bulk update of `{{ .StructName }}` in transaction 840 func (repo *{{ .RepositoryStructName }}) {{ UpdateMultiWithTxFunc }} { 841 {{- if eq .IsSubCollection true }} 842 if repo.collectionGroup != nil { 843 return ErrNotAvailableCG 844 } 845 {{- end }} 846 ctx = context.WithValue(ctx, transactionInProgressKey{}, tx) 847 848 for i := range subjects { 849 if err := repo.beforeUpdate(ctx, nil, subjects[i]); err != nil { 850 return xerrors.Errorf("before update error(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 851 } 852 } 853 854 for i := range subjects { 855 {{- if eq .EnableIndexes true}} 856 if err := repo.saveIndexes(subjects[i]); err != nil { 857 return xerrors.Errorf("failed to saveIndexes: %w", err) 858 } 859 {{- end }} 860 if err := repo.update(tx, subjects[i]); err != nil { 861 return xerrors.Errorf("error in update method(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 862 } 863 } 864 865 return nil 866 } 867 868 // DeleteMultiWithTx - bulk delete of `{{ .StructName }}` in transaction 869 func (repo *{{ .RepositoryStructName }}) {{ DeleteMultiWithTxFunc }} { 870 {{- if eq .IsSubCollection true }} 871 if repo.collectionGroup != nil { 872 return ErrNotAvailableCG 873 } 874 {{- end }} 875 {{ if .MetaFieldsEnabled }} 876 t := time.Now() 877 {{- end }} 878 var isHardDeleteMode bool 879 if len(opts) > 0 { 880 isHardDeleteMode = opts[0].Mode == DeleteModeHard 881 } 882 opt := GetOption{ 883 IncludeSoftDeleted: isHardDeleteMode, 884 } 885 for i := range subjects { 886 dr := repo.GetDocRef(subjects[i].{{ .KeyFieldName }}) 887 if _, err := repo.get(context.Background(), dr, opt); err != nil { 888 if status.Code(err) == codes.NotFound { 889 return xerrors.Errorf("not found(%d) [%v]", i, subjects[i].{{ .KeyFieldName }}) 890 } 891 return xerrors.Errorf("error in get method(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 892 } 893 894 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subjects[i], opts...); err != nil { 895 return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 896 } 897 {{- if .MetaFieldsEnabled }} 898 899 if !isHardDeleteMode { 900 subjects[i].DeletedAt = &t 901 if err := repo.update(tx, subjects[i]); err != nil { 902 return xerrors.Errorf("error in update method(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 903 } 904 } 905 {{- end }} 906 } 907 {{ if .MetaFieldsEnabled }} 908 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 909 return nil 910 } 911 {{ end }} 912 for i := range subjects { 913 if err := repo.deleteBy{{ .KeyFieldName }}(tx, subjects[i].{{ .KeyFieldName }}); err != nil { 914 return xerrors.Errorf("error in delete method(%d) [%v]: %w", i, subjects[i].{{ .KeyFieldName }}, err) 915 } 916 } 917 918 return nil 919 } 920 921 // DeleteMultiBy{{ .KeyFieldName }}WithTx - delete `{{ .StructName }}` in bulk by array of `{{ .StructName }}.{{ .KeyFieldName }}` in transaction 922 func (repo *{{ .RepositoryStructName }}) {{ DeleteMultiByWithTxFunc }} { 923 {{- if eq .IsSubCollection true }} 924 if repo.collectionGroup != nil { 925 return ErrNotAvailableCG 926 } 927 {{- end }} 928 {{ if .MetaFieldsEnabled }} 929 t := time.Now() 930 {{- end }} 931 for i := range {{ PluralForm .KeyValueName }} { 932 dr := repo.GetDocRef({{ PluralForm .KeyValueName }}[i]) 933 subject, err := repo.get(context.Background(), dr) 934 if err != nil { 935 if status.Code(err) == codes.NotFound { 936 return xerrors.Errorf("not found(%d) [%v]", i, {{ PluralForm .KeyValueName }}[i]) 937 } 938 return xerrors.Errorf("error in get method(%d) [%v]: %w", i, {{ PluralForm .KeyValueName }}[i], err) 939 } 940 941 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil { 942 return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subject.{{ .KeyFieldName }}, err) 943 } 944 {{- if .MetaFieldsEnabled }} 945 946 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 947 subject.DeletedAt = &t 948 if err := repo.update(tx, subject); err != nil { 949 return xerrors.Errorf("error in update method(%d) [%v]: %w", i, {{ PluralForm .KeyValueName }}[i], err) 950 } 951 } 952 {{- end }} 953 } 954 {{ if .MetaFieldsEnabled }} 955 if len(opts) > 0 && opts[0].Mode == DeleteModeSoft { 956 return nil 957 } 958 {{ end }} 959 for i := range {{ PluralForm .KeyValueName }} { 960 if err := repo.deleteBy{{ .KeyFieldName }}(tx, {{ PluralForm .KeyValueName }}[i]); err != nil { 961 return xerrors.Errorf("error in delete method(%d) [%v]: %w", i, {{ PluralForm .KeyValueName }}[i], err) 962 } 963 } 964 965 return nil 966 } 967 968 func (repo *{{ .RepositoryStructName }}) get(v interface{}, doc *firestore.DocumentRef, {{ if .MetaFieldsEnabled }}opts{{ else }}_{{ end }} ...GetOption) (*{{ .StructNameRef }}, error) { 969 var ( 970 snapShot *firestore.DocumentSnapshot 971 err error 972 ) 973 974 switch x := v.(type) { 975 case *firestore.Transaction: 976 snapShot, err = x.Get(doc) 977 case context.Context: 978 snapShot, err = doc.Get(x) 979 default: 980 return nil, xerrors.Errorf("invalid type: %v", x) 981 } 982 983 if err != nil { 984 if status.Code(err) == codes.NotFound { 985 return nil, ErrNotFound 986 } 987 return nil, xerrors.Errorf("error in Get method: %w", err) 988 } 989 990 subject := new({{ .StructNameRef }}) 991 if err := snapShot.DataTo(&subject); err != nil { 992 return nil, xerrors.Errorf("error in DataTo method: %w", err) 993 } 994 {{ if .MetaFieldsEnabled }} 995 if len(opts) == 0 || !opts[0].IncludeSoftDeleted { 996 if subject.DeletedAt != nil { 997 return nil, ErrAlreadyDeleted 998 } 999 } 1000 {{- end }} 1001 subject.{{ .KeyFieldName }} = snapShot.Ref.ID 1002 1003 return subject, nil 1004 } 1005 1006 func (repo *{{ .RepositoryStructName }}) getMulti(v interface{}, {{ PluralForm .KeyValueName }} []{{ .KeyFieldType }}, {{ if .MetaFieldsEnabled }}opts{{ else }}_{{ end }} ...GetOption) ([]*{{ .StructNameRef }}, error) { 1007 var ( 1008 snapShots []*firestore.DocumentSnapshot 1009 err error 1010 collect = repo.GetCollection() 1011 drs = make([]*firestore.DocumentRef, len({{ PluralForm .KeyValueName }})) 1012 ) 1013 1014 for i, {{ .KeyValueName }} := range {{ PluralForm .KeyValueName }} { 1015 ref := collect.Doc({{ .KeyValueName }}) 1016 drs[i] = ref 1017 } 1018 1019 switch x := v.(type) { 1020 case *firestore.Transaction: 1021 snapShots, err = x.GetAll(drs) 1022 case context.Context: 1023 snapShots, err = repo.firestoreClient.GetAll(x, drs) 1024 default: 1025 return nil, xerrors.Errorf("invalid type: %v", v) 1026 } 1027 1028 if err != nil { 1029 return nil, xerrors.Errorf("error in GetAll method: %w", err) 1030 } 1031 1032 subjects := make([]*{{ .StructNameRef }}, 0, len({{ PluralForm .KeyValueName }})) 1033 mErr := NewMultiErrors() 1034 for i, snapShot := range snapShots { 1035 if !snapShot.Exists() { 1036 mErr = append(mErr, NewMultiError(i, ErrNotFound)) 1037 continue 1038 } 1039 1040 subject := new({{ .StructNameRef }}) 1041 if err = snapShot.DataTo(&subject); err != nil { 1042 return nil, xerrors.Errorf("error in DataTo method: %w", err) 1043 } 1044 {{ if .MetaFieldsEnabled }} 1045 if len(opts) == 0 || !opts[0].IncludeSoftDeleted { 1046 if subject.DeletedAt != nil { 1047 mErr = append(mErr, NewMultiError(i, ErrLogicallyDeletedData)) 1048 continue 1049 } 1050 } 1051 {{- end }} 1052 subject.{{ .KeyFieldName }} = snapShot.Ref.ID 1053 subjects = append(subjects, subject) 1054 } 1055 1056 if len(mErr) == 0 { 1057 return subjects, nil 1058 } 1059 1060 return subjects, mErr 1061 } 1062 1063 func (repo *{{ .RepositoryStructName }}) insert(v interface{}, subject *{{ .StructNameRef }}) ({{ .KeyFieldType }}, error) { 1064 var ( 1065 {{- if .AutomaticGeneration }} 1066 dr = repo.GetCollection().NewDoc() 1067 {{- else }} 1068 dr = repo.GetDocRef(subject.{{ .KeyFieldName }}) 1069 {{- end }} 1070 err error 1071 ) 1072 1073 switch x := v.(type) { 1074 case *firestore.Transaction: 1075 err = x.Create(dr, subject) 1076 case context.Context: 1077 _, err = dr.Create(x, subject) 1078 default: 1079 return "", xerrors.Errorf("invalid type: %v", v) 1080 } 1081 1082 if err != nil { 1083 if status.Code(err) == codes.AlreadyExists { 1084 return "", xerrors.Errorf("error in Create method: err=%+v: %w", err, ErrAlreadyExists) 1085 } 1086 return "", xerrors.Errorf("error in Create method: %w", err) 1087 } 1088 1089 subject.{{ .KeyFieldName }} = dr.ID 1090 1091 return dr.ID, nil 1092 } 1093 1094 func (repo *{{ .RepositoryStructName }}) update(v interface{}, subject *{{ .StructNameRef }}) error { 1095 var ( 1096 dr = repo.GetDocRef(subject.{{ .KeyFieldName }}) 1097 err error 1098 ) 1099 1100 switch x := v.(type) { 1101 case *firestore.Transaction: 1102 err = x.Set(dr, subject) 1103 case context.Context: 1104 _, err = dr.Set(x, subject) 1105 default: 1106 return xerrors.Errorf("invalid type: %v", v) 1107 } 1108 1109 if err != nil { 1110 return xerrors.Errorf("error in Set method: %w", err) 1111 } 1112 1113 return nil 1114 } 1115 1116 func (repo *{{ .RepositoryStructName }}) strictUpdate(v interface{}, id string, param *{{ .StructName }}UpdateParam, opts ...firestore.Precondition) error { 1117 var ( 1118 dr = repo.GetDocRef(id) 1119 err error 1120 ) 1121 {{ if .MetaFieldsEnabled }} 1122 repo.setMetaWithStrictUpdate(param) 1123 {{ end }} 1124 updates := updater({{ .StructNameRef }}{}, param) 1125 1126 switch x := v.(type) { 1127 case *firestore.Transaction: 1128 err = x.Update(dr, updates, opts...) 1129 case context.Context: 1130 _, err = dr.Update(x, updates, opts...) 1131 default: 1132 return xerrors.Errorf("invalid type: %v", v) 1133 } 1134 1135 if err != nil { 1136 return xerrors.Errorf("error in Update method: %w", err) 1137 } 1138 1139 return nil 1140 } 1141 1142 func (repo *{{ .RepositoryStructName }}) deleteBy{{ .KeyFieldName }}(v interface{}, {{ .KeyValueName }} {{ .KeyFieldType }}) error { 1143 dr := repo.GetDocRef({{ .KeyValueName }}) 1144 var err error 1145 1146 switch x := v.(type) { 1147 case *firestore.Transaction: 1148 err = x.Delete(dr, firestore.Exists) 1149 case context.Context: 1150 _, err = dr.Delete(x, firestore.Exists) 1151 default: 1152 return xerrors.Errorf("invalid type: %v", v) 1153 } 1154 1155 if err != nil { 1156 return xerrors.Errorf("error in Delete method: %w", err) 1157 } 1158 1159 return nil 1160 } 1161 1162 func (repo *{{ .RepositoryStructName }}) runQuery(v interface{}, query firestore.Query) ([]*{{ .StructNameRef }}, error) { 1163 var iter *firestore.DocumentIterator 1164 1165 switch x := v.(type) { 1166 case *firestore.Transaction: 1167 iter = x.Documents(query) 1168 case context.Context: 1169 iter = query.Documents(x) 1170 default: 1171 return nil, xerrors.Errorf("invalid type: %v", v) 1172 } 1173 1174 defer iter.Stop() 1175 1176 subjects := make([]*{{ .StructNameRef }}, 0) 1177 1178 for { 1179 doc, err := iter.Next() 1180 if err == iterator.Done { 1181 break 1182 } 1183 if err != nil { 1184 return nil, xerrors.Errorf("error in Next method: %w", err) 1185 } 1186 1187 subject := new({{ .StructNameRef }}) 1188 1189 if err = doc.DataTo(&subject); err != nil { 1190 return nil, xerrors.Errorf("error in DataTo method: %w", err) 1191 } 1192 1193 subject.{{ .KeyFieldName }} = doc.Ref.ID 1194 subjects = append(subjects, subject) 1195 } 1196 1197 return subjects, nil 1198 } 1199 1200 // BUG(54m): there may be potential bugs 1201 func (repo *{{ .RepositoryStructName }}) searchByParam(v interface{}, param *{{ .StructName }}SearchParam) ([]*{{ .StructNameRef }}, *PagingResult, error) { 1202 query := func() firestore.Query { 1203 {{- if eq .IsSubCollection true }} 1204 if repo.collectionGroup != nil { 1205 return repo.collectionGroup.Query 1206 } 1207 {{- end }} 1208 return repo.GetCollection().Query 1209 }() 1210 {{- if .EnableIndexes }} 1211 filters := xim.NewFilters(&xim.Config{ 1212 IgnoreCase: true, 1213 SaveNoFiltersIndex: true, 1214 }) 1215 {{ end }} 1216 {{- $Meta := .MetaFieldsEnabled }} 1217 {{- $EnableIndexes := .EnableIndexes }} 1218 {{- range $fi := .FieldInfos }} 1219 {{- $IsSlice := HasSlice $fi.FieldType }} 1220 {{- $IsMap := HasMap $fi.FieldType }} 1221 if param.{{ $fi.Field }} != nil { 1222 {{- if eq $IsMap true }} 1223 for _, chain := range param.{{ $fi.Field }}.QueryGroup { 1224 items, ok := chain.Value.({{ PrimitiveMap $fi.FieldType }}) 1225 if !ok { 1226 continue 1227 } 1228 for key, value := range items { 1229 query = query.WherePath(firestore.FieldPath{{ $bracketsStart }}"{{ $fi.FsTag }}", key{{ $bracketsEnd }}, chain.Operator, value) 1230 } 1231 } 1232 {{- else if eq $IsSlice true }} 1233 for _, chain := range param.{{ $fi.Field }}.QueryGroup { 1234 query = query.Where("{{ $fi.FsTag }}", chain.Operator, chain.Value) 1235 } 1236 {{- else if $fi.IsDocumentID }} 1237 for _, chain := range param.{{ $fi.Field }}.QueryGroup { 1238 var value interface{} 1239 switch val := chain.Value.(type) { 1240 case string: 1241 value = repo.GetDocRef(val) 1242 case []string: 1243 docRefs := make([]*firestore.DocumentRef, len(val)) 1244 for i := range val { 1245 docRefs[i] = repo.GetDocRef(val[i]) 1246 } 1247 value = docRefs 1248 default: 1249 return nil, nil, xerrors.Errorf("document id can only be of type `string` and `[]string`. value: %#v", chain.Value) 1250 } 1251 query = query.Where(firestore.DocumentID, chain.Operator, value) 1252 } 1253 if direction := param.{{ $fi.Field }}.OrderByDirection; direction > 0 { 1254 query = query.OrderBy(firestore.DocumentID, direction) 1255 query = param.{{ $fi.Field }}.BuildCursorQuery(query) 1256 } 1257 {{- else }} 1258 for _, chain := range param.{{ $fi.Field }}.QueryGroup { 1259 query = query.Where("{{ $fi.FsTag }}", chain.Operator, chain.Value) 1260 } 1261 if direction := param.{{ $fi.Field }}.OrderByDirection; direction > 0 { 1262 query = query.OrderBy("{{ $fi.FsTag }}", direction) 1263 query = param.{{ $fi.Field }}.BuildCursorQuery(query) 1264 } 1265 {{- if and (eq $EnableIndexes true ) (ne ( len $fi.Indexes ) 0) }} 1266 value, ok := param.{{ $fi.Field }}.Filter.Value.(string) 1267 {{- $indexerInfo := IndexerInfo $fi }} 1268 {{- if ne $indexerInfo "" }} 1269 {{ print $indexerInfo }} 1270 {{- end }} 1271 for _, filter := range param.{{ $fi.Field }}.Filter.FilterTypes { 1272 switch filter { 1273 {{- $EqualLabel := "" }} 1274 {{- range $filters := $fi.Indexes }} 1275 {{- if eq (HasSuffix $filters.ConstName "Equal") true }} 1276 {{- $EqualLabel = $filters.ConstName }} 1277 {{- end }} 1278 {{- if and (ne $filters.Method "AddSomething" ) (eq $filters.Use true) }} 1279 {{- if ne $filters.Method "Add" }} 1280 case {{ if eq $filters.Method "AddPrefix" }}FilterTypeAddPrefix{{ else if eq $filters.Method "AddSuffix" }}FilterTypeAddSuffix{{ else if eq $filters.Method "AddBiunigrams" }}FilterTypeAddBiunigrams{{ end }}: 1281 if ok { 1282 filters.{{ $filters.Method }}({{ $filters.ConstName }}, value) 1283 } 1284 {{- end }} 1285 {{- end }} 1286 {{- end }} 1287 // Treat `Add` or otherwise as `Equal`. 1288 case FilterTypeAdd: 1289 fallthrough 1290 default: 1291 if !ok { 1292 filters.AddSomething({{ $EqualLabel }}, param.{{ $fi.Field }}.Filter.Value) 1293 continue 1294 } 1295 filters.Add({{ $EqualLabel }}, value) 1296 } 1297 } 1298 {{- end }} 1299 {{- end }} 1300 } 1301 {{- end }} 1302 {{- if .MetaFieldsEnabled }} 1303 if !param.IncludeSoftDeleted { 1304 query = query.Where("{{ (LookUpFieldByName .FieldInfos "DeletedAt").FsTag }}", OpTypeEqual, nil) 1305 } 1306 {{- end }} 1307 {{ if .EnableIndexes }} 1308 build, err := filters.Build() 1309 if err != nil { 1310 return nil, nil, xerrors.Errorf("failed to filter build: %w", err) 1311 } 1312 for key := range build { 1313 query = query.WherePath(firestore.FieldPath{{ $bracketsStart }}"{{ .FieldInfoForIndexes.FsTag }}", key{{ $bracketsEnd}}, OpTypeEqual, true) 1314 } 1315 {{ end }} 1316 limit := param.CursorLimit + 1 1317 1318 if param.CursorKey != "" { 1319 var ( 1320 ds *firestore.DocumentSnapshot 1321 err error 1322 ) 1323 switch x := v.(type) { 1324 case *firestore.Transaction: 1325 ds, err = x.Get(repo.GetDocRef(param.CursorKey)) 1326 case context.Context: 1327 ds, err = repo.GetDocRef(param.CursorKey).Get(x) 1328 default: 1329 return nil, nil, xerrors.Errorf("invalid x type: %v", v) 1330 } 1331 if err != nil { 1332 if status.Code(err) == codes.NotFound { 1333 return nil, nil, ErrNotFound 1334 } 1335 return nil, nil, xerrors.Errorf("error in Get method: %w", err) 1336 } 1337 query = query.StartAt(ds) 1338 } 1339 1340 if limit > 1 { 1341 query = query.Limit(limit) 1342 } 1343 1344 subjects, err := repo.runQuery(v, query) 1345 if err != nil { 1346 return nil, nil, xerrors.Errorf("error in runQuery method: %w", err) 1347 } 1348 1349 pagingResult := &PagingResult{ 1350 Length: len(subjects), 1351 } 1352 if limit > 1 && limit == pagingResult.Length { 1353 next := pagingResult.Length-1 1354 pagingResult.NextCursorKey = subjects[next].{{ .KeyFieldName }} 1355 subjects = subjects[:next] 1356 pagingResult.Length-- 1357 } 1358 1359 return subjects, pagingResult, nil 1360 } 1361 1362 func (repo *{{ .RepositoryStructName }}) search(v interface{}, param *{{ .StructName }}SearchParam, q *firestore.Query) ([]*{{ .StructNameRef }}, error) { 1363 if (param == nil && q == nil) || (param != nil && q != nil) { 1364 return nil, xerrors.New("either one should be nil") 1365 } 1366 1367 query := func() firestore.Query { 1368 if q != nil { 1369 return *q 1370 } 1371 {{- if eq .IsSubCollection true }} 1372 if repo.collectionGroup != nil { 1373 return repo.collectionGroup.Query 1374 } 1375 {{- end }} 1376 return repo.GetCollection().Query 1377 }() 1378 1379 if q == nil { 1380 subjects, _, err := repo.searchByParam(v, param) 1381 if err != nil { 1382 return nil, xerrors.Errorf("error in searchByParam method: %w", err) 1383 } 1384 1385 return subjects, nil 1386 } 1387 1388 return repo.runQuery(v, query) 1389 } 1390 {{- if ne ( len .UniqueInfos ) 0 }} 1391 {{- $repositoryStructName := .RepositoryStructName }} 1392 {{- $metaFiledsEnabled := .MetaFieldsEnabled }} 1393 {{- range $ui := .UniqueInfos }} 1394 1395 // GetBy{{ $ui.Field }} - get by {{ $ui.Field }} 1396 func (repo *{{ $repositoryStructName }}) GetBy{{ $ui.Field }}(ctx context.Context, {{ ToLower $ui.FsTag }} string{{ if $metaFiledsEnabled }}, opts ...GetOption{{ end }}) (*{{ $structNameRef }}, error) { 1397 return repo.getByXXX(ctx, "{{ $ui.FsTag }}", {{ ToLower $ui.FsTag }}{{ if $metaFiledsEnabled }}, opts...{{ end }}) 1398 } 1399 1400 // GetBy{{ $ui.Field }}WithTx - get by {{ $ui.Field }} in transaction 1401 func (repo *{{ $repositoryStructName }}) GetBy{{ $ui.Field }}WithTx(tx *firestore.Transaction, {{ ToLower $ui.FsTag }} string{{ if $metaFiledsEnabled }}, opts ...GetOption{{ end }}) (*{{ $structNameRef }}, error) { 1402 return repo.getByXXX(tx, "{{ $ui.FsTag }}", {{ ToLower $ui.FsTag }}{{ if $metaFiledsEnabled }}, opts...{{ end }}) 1403 } 1404 {{- end }} 1405 1406 func (repo *{{ .RepositoryStructName }}) getByXXX(v interface{}, field, value string{{ if .MetaFieldsEnabled }}, opts ...GetOption{{ end }}) (*{{ $structNameRef }}, error) { 1407 query := repo.GetCollection().Query.Where(field, OpTypeEqual, value){{ if not .MetaFieldsEnabled }}.Limit(1){{ end }} 1408 {{- if .MetaFieldsEnabled }} 1409 if len(opts) == 0 || !opts[0].IncludeSoftDeleted { 1410 query = query.Where("{{ (LookUpFieldByName .FieldInfos "DeletedAt").FsTag }}", OpTypeEqual, nil) 1411 } 1412 query = query.Limit(1) 1413 {{- end }} 1414 results, err := repo.runQuery(v, query) 1415 if err != nil { 1416 return nil, xerrors.Errorf("failed to run query: %w", err) 1417 } else if len(results) == 0 { 1418 return nil, ErrNotFound 1419 } 1420 return results[0], nil 1421 } 1422 {{- end }}