github.com/go-generalize/volcago@v1.7.0/generator/testfiles/auto/task_gen.go (about) 1 // Code generated by volcago. DO NOT EDIT. 2 // generated version: devel 3 package model 4 5 import ( 6 "context" 7 8 "cloud.google.com/go/firestore" 9 "github.com/go-utils/xim" 10 "golang.org/x/xerrors" 11 "google.golang.org/api/iterator" 12 "google.golang.org/grpc/codes" 13 "google.golang.org/grpc/status" 14 ) 15 16 //go:generate mockgen -source $GOFILE -destination mock/mock_task_gen/mock_task_gen.go 17 18 // TaskRepository - Repository of Task 19 type TaskRepository interface { 20 // Single 21 Get(ctx context.Context, id string, opts ...GetOption) (*Task, error) 22 GetWithDoc(ctx context.Context, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error) 23 Insert(ctx context.Context, subject *Task) (_ string, err error) 24 Update(ctx context.Context, subject *Task) (err error) 25 StrictUpdate(ctx context.Context, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error 26 Delete(ctx context.Context, subject *Task, opts ...DeleteOption) (err error) 27 DeleteByID(ctx context.Context, id string, opts ...DeleteOption) (err error) 28 // Multiple 29 GetMulti(ctx context.Context, ids []string, opts ...GetOption) ([]*Task, error) 30 InsertMulti(ctx context.Context, subjects []*Task) (_ []string, er error) 31 UpdateMulti(ctx context.Context, subjects []*Task) (er error) 32 DeleteMulti(ctx context.Context, subjects []*Task, opts ...DeleteOption) (er error) 33 DeleteMultiByIDs(ctx context.Context, ids []string, opts ...DeleteOption) (er error) 34 // Single(Transaction) 35 GetWithTx(tx *firestore.Transaction, id string, opts ...GetOption) (*Task, error) 36 GetWithDocWithTx(tx *firestore.Transaction, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error) 37 InsertWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (_ string, err error) 38 UpdateWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (err error) 39 StrictUpdateWithTx(tx *firestore.Transaction, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error 40 DeleteWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task, opts ...DeleteOption) (err error) 41 DeleteByIDWithTx(ctx context.Context, tx *firestore.Transaction, id string, opts ...DeleteOption) (err error) 42 // Multiple(Transaction) 43 GetMultiWithTx(tx *firestore.Transaction, ids []string, opts ...GetOption) ([]*Task, error) 44 InsertMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (_ []string, er error) 45 UpdateMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (er error) 46 DeleteMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task, opts ...DeleteOption) (er error) 47 DeleteMultiByIDsWithTx(ctx context.Context, tx *firestore.Transaction, ids []string, opts ...DeleteOption) (er error) 48 // Search 49 Search(ctx context.Context, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) 50 SearchWithTx(tx *firestore.Transaction, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) 51 SearchByParam(ctx context.Context, param *TaskSearchParam) ([]*Task, *PagingResult, error) 52 SearchByParamWithTx(tx *firestore.Transaction, param *TaskSearchParam) ([]*Task, *PagingResult, error) 53 // misc 54 GetCollection() *firestore.CollectionRef 55 GetCollectionName() string 56 GetDocRef(id string) *firestore.DocumentRef 57 RunInTransaction() func(ctx context.Context, f func(context.Context, *firestore.Transaction) error, opts ...firestore.TransactionOption) (err error) 58 } 59 60 // TaskRepositoryMiddleware - middleware of TaskRepository 61 type TaskRepositoryMiddleware interface { 62 BeforeInsert(ctx context.Context, subject *Task) (bool, error) 63 BeforeUpdate(ctx context.Context, old, subject *Task) (bool, error) 64 BeforeDelete(ctx context.Context, subject *Task, opts ...DeleteOption) (bool, error) 65 BeforeDeleteByID(ctx context.Context, ids []string, opts ...DeleteOption) (bool, error) 66 } 67 68 type taskRepository struct { 69 collectionName string 70 firestoreClient *firestore.Client 71 middleware []TaskRepositoryMiddleware 72 uniqueRepository *uniqueRepository 73 } 74 75 // NewTaskRepository - constructor 76 func NewTaskRepository(firestoreClient *firestore.Client, middleware ...TaskRepositoryMiddleware) TaskRepository { 77 return &taskRepository{ 78 collectionName: "Task", 79 firestoreClient: firestoreClient, 80 middleware: middleware, 81 uniqueRepository: newUniqueRepository(firestoreClient, "Task"), 82 } 83 } 84 85 func (repo *taskRepository) beforeInsert(ctx context.Context, subject *Task) error { 86 repo.uniqueRepository.setMiddleware(ctx) 87 err := repo.uniqueRepository.CheckUnique(ctx, nil, subject) 88 if err != nil { 89 return xerrors.Errorf("unique.middleware error: %w", err) 90 } 91 92 for _, m := range repo.middleware { 93 c, err := m.BeforeInsert(ctx, subject) 94 if err != nil { 95 return xerrors.Errorf("beforeInsert.middleware error: %w", err) 96 } 97 if !c { 98 continue 99 } 100 } 101 102 return nil 103 } 104 105 func (repo *taskRepository) beforeUpdate(ctx context.Context, old, subject *Task) error { 106 if ctx.Value(transactionInProgressKey{}) != nil && old == nil { 107 var err error 108 doc := repo.GetDocRef(subject.ID) 109 old, err = repo.get(context.Background(), doc) 110 if err != nil { 111 if status.Code(err) == codes.NotFound { 112 return ErrNotFound 113 } 114 return xerrors.Errorf("error in Get method: %w", err) 115 } 116 } 117 repo.uniqueRepository.setMiddleware(ctx) 118 err := repo.uniqueRepository.CheckUnique(ctx, old, subject) 119 if err != nil { 120 return xerrors.Errorf("unique.middleware error: %w", err) 121 } 122 123 for _, m := range repo.middleware { 124 c, err := m.BeforeUpdate(ctx, old, subject) 125 if err != nil { 126 return xerrors.Errorf("beforeUpdate.middleware error: %w", err) 127 } 128 if !c { 129 continue 130 } 131 } 132 133 return nil 134 } 135 136 func (repo *taskRepository) beforeDelete(ctx context.Context, subject *Task, opts ...DeleteOption) error { 137 repo.uniqueRepository.setMiddleware(ctx) 138 err := repo.uniqueRepository.DeleteUnique(ctx, subject) 139 if err != nil { 140 return xerrors.Errorf("unique.middleware error: %w", err) 141 } 142 143 for _, m := range repo.middleware { 144 c, err := m.BeforeDelete(ctx, subject, opts...) 145 if err != nil { 146 return xerrors.Errorf("beforeDelete.middleware error: %w", err) 147 } 148 if !c { 149 continue 150 } 151 } 152 153 return nil 154 } 155 156 // GetCollection - *firestore.CollectionRef getter 157 func (repo *taskRepository) GetCollection() *firestore.CollectionRef { 158 return repo.firestoreClient.Collection(repo.collectionName) 159 } 160 161 // GetCollectionName - CollectionName getter 162 func (repo *taskRepository) GetCollectionName() string { 163 return repo.collectionName 164 } 165 166 // GetDocRef - *firestore.DocumentRef getter 167 func (repo *taskRepository) GetDocRef(id string) *firestore.DocumentRef { 168 return repo.GetCollection().Doc(id) 169 } 170 171 // RunInTransaction - (*firestore.Client).RunTransaction getter 172 func (repo *taskRepository) RunInTransaction() func(ctx context.Context, f func(context.Context, *firestore.Transaction) error, opts ...firestore.TransactionOption) (err error) { 173 return repo.firestoreClient.RunTransaction 174 } 175 176 func (repo *taskRepository) saveIndexes(subject *Task) error { 177 idx := xim.NewIndexes(&xim.Config{ 178 IgnoreCase: true, 179 SaveNoFiltersIndex: true, 180 }) 181 { 182 idx.Add(TaskIndexLabelDescEqual, string(subject.Desc)) 183 idx.AddBiunigrams(TaskIndexLabelDescLike, string(subject.Desc)) 184 idx.AddPrefixes(TaskIndexLabelDescPrefix, string(subject.Desc)) 185 idx.AddSuffixes(TaskIndexLabelDescSuffix, string(subject.Desc)) 186 idx.AddSomething(TaskIndexLabelProportionEqual, subject.Proportion) 187 } 188 indexes, err := idx.Build() 189 if err != nil { 190 return xerrors.Errorf("failed to index build: %w", err) 191 } else if len(indexes) == 0 { 192 return nil 193 } 194 195 subject.Indexes = indexes 196 197 return nil 198 } 199 200 // TaskSearchParam - params for search 201 type TaskSearchParam struct { 202 ID *QueryChainer 203 Desc *QueryChainer 204 Created *QueryChainer 205 Done *QueryChainer 206 Done2 *QueryChainer 207 Count *QueryChainer 208 Count64 *QueryChainer 209 NameList *QueryChainer 210 Proportion *QueryChainer 211 Flag *QueryChainer 212 SliceSubTask *QueryChainer 213 214 CursorKey string 215 CursorLimit int 216 } 217 218 // TaskUpdateParam - params for strict updates 219 type TaskUpdateParam struct { 220 Desc interface{} 221 Created interface{} 222 Done interface{} 223 Done2 interface{} 224 Count interface{} 225 Count64 interface{} 226 NameList interface{} 227 Proportion interface{} 228 Flag interface{} 229 SliceSubTask interface{} 230 } 231 232 // Search - search documents 233 // The third argument is firestore.Query, basically you can pass nil 234 func (repo *taskRepository) Search(ctx context.Context, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) { 235 return repo.search(ctx, param, q) 236 } 237 238 // SearchByParam - search documents by search param 239 func (repo *taskRepository) SearchByParam(ctx context.Context, param *TaskSearchParam) ([]*Task, *PagingResult, error) { 240 return repo.searchByParam(ctx, param) 241 } 242 243 // Get - get `Task` by `Task.ID` 244 func (repo *taskRepository) Get(ctx context.Context, id string, opts ...GetOption) (*Task, error) { 245 doc := repo.GetDocRef(id) 246 return repo.get(ctx, doc, opts...) 247 } 248 249 // GetWithDoc - get `Task` by *firestore.DocumentRef 250 func (repo *taskRepository) GetWithDoc(ctx context.Context, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error) { 251 return repo.get(ctx, doc, opts...) 252 } 253 254 // Insert - insert of `Task` 255 func (repo *taskRepository) Insert(ctx context.Context, subject *Task) (_ string, err error) { 256 if err := repo.beforeInsert(ctx, subject); err != nil { 257 return "", xerrors.Errorf("before insert error: %w", err) 258 } 259 260 if err = repo.saveIndexes(subject); err != nil { 261 return "", xerrors.Errorf("failed to saveIndexes: %w", err) 262 } 263 264 return repo.insert(ctx, subject) 265 } 266 267 // Update - update of `Task` 268 func (repo *taskRepository) Update(ctx context.Context, subject *Task) (err error) { 269 doc := repo.GetDocRef(subject.ID) 270 271 old, err := repo.get(ctx, doc) 272 if err != nil { 273 if status.Code(err) == codes.NotFound { 274 return ErrNotFound 275 } 276 return xerrors.Errorf("error in Get method: %w", err) 277 } 278 279 if err := repo.beforeUpdate(ctx, old, subject); err != nil { 280 return xerrors.Errorf("before update error: %w", err) 281 } 282 283 if err := repo.saveIndexes(subject); err != nil { 284 return xerrors.Errorf("failed to saveIndexes: %w", err) 285 } 286 287 return repo.update(ctx, subject) 288 } 289 290 // StrictUpdate - strict update of `Task` 291 func (repo *taskRepository) StrictUpdate(ctx context.Context, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error { 292 return repo.strictUpdate(ctx, id, param, opts...) 293 } 294 295 // Delete - delete of `Task` 296 func (repo *taskRepository) Delete(ctx context.Context, subject *Task, opts ...DeleteOption) (err error) { 297 if err := repo.beforeDelete(ctx, subject, opts...); err != nil { 298 return xerrors.Errorf("before delete error: %w", err) 299 } 300 301 return repo.deleteByID(ctx, subject.ID) 302 } 303 304 // DeleteByID - delete `Task` by `Task.ID` 305 func (repo *taskRepository) DeleteByID(ctx context.Context, id string, opts ...DeleteOption) (err error) { 306 subject, err := repo.Get(ctx, id) 307 if err != nil { 308 return xerrors.Errorf("error in Get method: %w", err) 309 } 310 311 if err := repo.beforeDelete(ctx, subject, opts...); err != nil { 312 return xerrors.Errorf("before delete error: %w", err) 313 } 314 315 return repo.Delete(ctx, subject, opts...) 316 } 317 318 // GetMulti - get `Task` in bulk by array of `Task.ID` 319 func (repo *taskRepository) GetMulti(ctx context.Context, ids []string, opts ...GetOption) ([]*Task, error) { 320 return repo.getMulti(ctx, ids, opts...) 321 } 322 323 // InsertMulti - bulk insert of `Task` 324 func (repo *taskRepository) InsertMulti(ctx context.Context, subjects []*Task) (_ []string, er error) { 325 326 ids := make([]string, 0, len(subjects)) 327 batches := make([]*firestore.WriteBatch, 0) 328 batch := repo.firestoreClient.Batch() 329 collect := repo.GetCollection() 330 331 for i, subject := range subjects { 332 var ref *firestore.DocumentRef 333 if subject.ID == "" { 334 ref = collect.NewDoc() 335 subject.ID = ref.ID 336 } else { 337 ref = collect.Doc(subject.ID) 338 if s, err := ref.Get(ctx); err == nil { 339 return nil, xerrors.Errorf("already exists [%v]: %#v", subject.ID, s) 340 } 341 } 342 343 if err := repo.beforeInsert(ctx, subject); err != nil { 344 return nil, xerrors.Errorf("before insert error(%d) [%v]: %w", i, subject.ID, err) 345 } 346 347 if err := repo.saveIndexes(subjects[i]); err != nil { 348 return nil, xerrors.Errorf("failed to saveIndexes: %w", err) 349 } 350 351 batch.Set(ref, subject) 352 ids = append(ids, ref.ID) 353 i++ 354 if (i%500) == 0 && len(subjects) != i { 355 batches = append(batches, batch) 356 batch = repo.firestoreClient.Batch() 357 } 358 } 359 batches = append(batches, batch) 360 361 for _, b := range batches { 362 if _, err := b.Commit(ctx); err != nil { 363 return nil, xerrors.Errorf("error in Commit method: %w", err) 364 } 365 } 366 367 return ids, nil 368 } 369 370 // UpdateMulti - bulk update of `Task` 371 func (repo *taskRepository) UpdateMulti(ctx context.Context, subjects []*Task) (er error) { 372 373 batches := make([]*firestore.WriteBatch, 0) 374 batch := repo.firestoreClient.Batch() 375 collect := repo.GetCollection() 376 377 for i, subject := range subjects { 378 ref := collect.Doc(subject.ID) 379 snapShot, err := ref.Get(ctx) 380 if err != nil { 381 if status.Code(err) == codes.NotFound { 382 return xerrors.Errorf("not found [%v]: %w", subject.ID, err) 383 } 384 return xerrors.Errorf("error in Get method [%v]: %w", subject.ID, err) 385 } 386 387 old := new(Task) 388 if err = snapShot.DataTo(&old); err != nil { 389 return xerrors.Errorf("error in DataTo method: %w", err) 390 } 391 392 if err := repo.beforeUpdate(ctx, old, subject); err != nil { 393 return xerrors.Errorf("before update error(%d) [%v]: %w", i, subject.ID, err) 394 } 395 396 if err := repo.saveIndexes(subjects[i]); err != nil { 397 return xerrors.Errorf("failed to saveIndexes: %w", err) 398 } 399 400 batch.Set(ref, subject) 401 i++ 402 if (i%500) == 0 && len(subjects) != i { 403 batches = append(batches, batch) 404 batch = repo.firestoreClient.Batch() 405 } 406 } 407 batches = append(batches, batch) 408 409 for _, b := range batches { 410 if _, err := b.Commit(ctx); err != nil { 411 return xerrors.Errorf("error in Commit method: %w", err) 412 } 413 } 414 415 return nil 416 } 417 418 // DeleteMulti - bulk delete of `Task` 419 func (repo *taskRepository) DeleteMulti(ctx context.Context, subjects []*Task, opts ...DeleteOption) (er error) { 420 421 batches := make([]*firestore.WriteBatch, 0) 422 batch := repo.firestoreClient.Batch() 423 collect := repo.GetCollection() 424 425 for i, subject := range subjects { 426 ref := collect.Doc(subject.ID) 427 if _, err := ref.Get(ctx); err != nil { 428 if status.Code(err) == codes.NotFound { 429 return xerrors.Errorf("not found [%v]: %w", subject.ID, err) 430 } 431 return xerrors.Errorf("error in Get method [%v]: %w", subject.ID, err) 432 } 433 434 if err := repo.beforeDelete(ctx, subject, opts...); err != nil { 435 return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subject.ID, err) 436 } 437 438 batch.Delete(ref) 439 440 i++ 441 if (i%500) == 0 && len(subjects) != i { 442 batches = append(batches, batch) 443 batch = repo.firestoreClient.Batch() 444 } 445 } 446 batches = append(batches, batch) 447 448 for _, b := range batches { 449 if _, err := b.Commit(ctx); err != nil { 450 return xerrors.Errorf("error in Commit method: %w", err) 451 } 452 } 453 454 return nil 455 } 456 457 // DeleteMultiByIDs - delete `Task` in bulk by array of `Task.ID` 458 func (repo *taskRepository) DeleteMultiByIDs(ctx context.Context, ids []string, opts ...DeleteOption) (er error) { 459 subjects := make([]*Task, len(ids)) 460 461 opt := GetOption{} 462 if len(opts) > 0 { 463 opt.IncludeSoftDeleted = opts[0].Mode == DeleteModeHard 464 } 465 for i, id := range ids { 466 subject, err := repo.Get(ctx, id, opt) 467 if err != nil { 468 return xerrors.Errorf("error in Get method: %w", err) 469 } 470 subjects[i] = subject 471 } 472 473 return repo.DeleteMulti(ctx, subjects, opts...) 474 } 475 476 // SearchWithTx - search documents in transaction 477 func (repo *taskRepository) SearchWithTx(tx *firestore.Transaction, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) { 478 return repo.search(tx, param, q) 479 } 480 481 // SearchByParamWithTx - search documents by search param in transaction 482 func (repo *taskRepository) SearchByParamWithTx(tx *firestore.Transaction, param *TaskSearchParam) ([]*Task, *PagingResult, error) { 483 return repo.searchByParam(tx, param) 484 } 485 486 // GetWithTx - get `Task` by `Task.ID` in transaction 487 func (repo *taskRepository) GetWithTx(tx *firestore.Transaction, id string, opts ...GetOption) (*Task, error) { 488 doc := repo.GetDocRef(id) 489 return repo.get(tx, doc, opts...) 490 } 491 492 // GetWithDocWithTx - get `Task` by *firestore.DocumentRef in transaction 493 func (repo *taskRepository) GetWithDocWithTx(tx *firestore.Transaction, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error) { 494 return repo.get(tx, doc, opts...) 495 } 496 497 // InsertWithTx - insert of `Task` in transaction 498 func (repo *taskRepository) InsertWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (_ string, err error) { 499 if err := repo.beforeInsert(context.WithValue(ctx, transactionInProgressKey{}, tx), subject); err != nil { 500 return "", xerrors.Errorf("before insert error: %w", err) 501 } 502 503 if err := repo.saveIndexes(subject); err != nil { 504 return "", xerrors.Errorf("failed to saveIndexes: %w", err) 505 } 506 507 return repo.insert(tx, subject) 508 } 509 510 // UpdateWithTx - update of `Task` in transaction 511 func (repo *taskRepository) UpdateWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (err error) { 512 if err := repo.beforeUpdate(context.WithValue(ctx, transactionInProgressKey{}, tx), nil, subject); err != nil { 513 return xerrors.Errorf("before update error: %w", err) 514 } 515 516 if err := repo.saveIndexes(subject); err != nil { 517 return xerrors.Errorf("failed to saveIndexes: %w", err) 518 } 519 520 return repo.update(tx, subject) 521 } 522 523 // StrictUpdateWithTx - strict update of `Task` in transaction 524 func (repo *taskRepository) StrictUpdateWithTx(tx *firestore.Transaction, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error { 525 return repo.strictUpdate(tx, id, param, opts...) 526 } 527 528 // DeleteWithTx - delete of `Task` in transaction 529 func (repo *taskRepository) DeleteWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task, opts ...DeleteOption) (err error) { 530 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil { 531 return xerrors.Errorf("before delete error: %w", err) 532 } 533 534 return repo.deleteByID(tx, subject.ID) 535 } 536 537 // DeleteByIDWithTx - delete `Task` by `Task.ID` in transaction 538 func (repo *taskRepository) DeleteByIDWithTx(ctx context.Context, tx *firestore.Transaction, id string, opts ...DeleteOption) (err error) { 539 subject, err := repo.Get(context.Background(), id) 540 if err != nil { 541 return xerrors.Errorf("error in Get method: %w", err) 542 } 543 544 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil { 545 return xerrors.Errorf("before delete error: %w", err) 546 } 547 548 return repo.deleteByID(tx, id) 549 } 550 551 // GetMultiWithTx - get `Task` in bulk by array of `Task.ID` in transaction 552 func (repo *taskRepository) GetMultiWithTx(tx *firestore.Transaction, ids []string, opts ...GetOption) ([]*Task, error) { 553 return repo.getMulti(tx, ids, opts...) 554 } 555 556 // InsertMultiWithTx - bulk insert of `Task` in transaction 557 func (repo *taskRepository) InsertMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (_ []string, er error) { 558 559 ids := make([]string, len(subjects)) 560 561 for i := range subjects { 562 if err := repo.beforeInsert(ctx, subjects[i]); err != nil { 563 return nil, xerrors.Errorf("before insert error(%d) [%v]: %w", i, subjects[i].ID, err) 564 } 565 566 if err := repo.saveIndexes(subjects[i]); err != nil { 567 return nil, xerrors.Errorf("failed to saveIndexes: %w", err) 568 } 569 570 id, err := repo.insert(tx, subjects[i]) 571 if err != nil { 572 return nil, xerrors.Errorf("error in insert method(%d) [%v]: %w", i, subjects[i].ID, err) 573 } 574 ids[i] = id 575 } 576 577 return ids, nil 578 } 579 580 // UpdateMultiWithTx - bulk update of `Task` in transaction 581 func (repo *taskRepository) UpdateMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (er error) { 582 ctx = context.WithValue(ctx, transactionInProgressKey{}, tx) 583 584 for i := range subjects { 585 if err := repo.beforeUpdate(ctx, nil, subjects[i]); err != nil { 586 return xerrors.Errorf("before update error(%d) [%v]: %w", i, subjects[i].ID, err) 587 } 588 } 589 590 for i := range subjects { 591 if err := repo.saveIndexes(subjects[i]); err != nil { 592 return xerrors.Errorf("failed to saveIndexes: %w", err) 593 } 594 if err := repo.update(tx, subjects[i]); err != nil { 595 return xerrors.Errorf("error in update method(%d) [%v]: %w", i, subjects[i].ID, err) 596 } 597 } 598 599 return nil 600 } 601 602 // DeleteMultiWithTx - bulk delete of `Task` in transaction 603 func (repo *taskRepository) DeleteMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task, opts ...DeleteOption) (er error) { 604 605 var isHardDeleteMode bool 606 if len(opts) > 0 { 607 isHardDeleteMode = opts[0].Mode == DeleteModeHard 608 } 609 opt := GetOption{ 610 IncludeSoftDeleted: isHardDeleteMode, 611 } 612 for i := range subjects { 613 dr := repo.GetDocRef(subjects[i].ID) 614 if _, err := repo.get(context.Background(), dr, opt); err != nil { 615 if status.Code(err) == codes.NotFound { 616 return xerrors.Errorf("not found(%d) [%v]", i, subjects[i].ID) 617 } 618 return xerrors.Errorf("error in get method(%d) [%v]: %w", i, subjects[i].ID, err) 619 } 620 621 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subjects[i], opts...); err != nil { 622 return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subjects[i].ID, err) 623 } 624 } 625 626 for i := range subjects { 627 if err := repo.deleteByID(tx, subjects[i].ID); err != nil { 628 return xerrors.Errorf("error in delete method(%d) [%v]: %w", i, subjects[i].ID, err) 629 } 630 } 631 632 return nil 633 } 634 635 // DeleteMultiByIDWithTx - delete `Task` in bulk by array of `Task.ID` in transaction 636 func (repo *taskRepository) DeleteMultiByIDsWithTx(ctx context.Context, tx *firestore.Transaction, ids []string, opts ...DeleteOption) (er error) { 637 638 for i := range ids { 639 dr := repo.GetDocRef(ids[i]) 640 subject, err := repo.get(context.Background(), dr) 641 if err != nil { 642 if status.Code(err) == codes.NotFound { 643 return xerrors.Errorf("not found(%d) [%v]", i, ids[i]) 644 } 645 return xerrors.Errorf("error in get method(%d) [%v]: %w", i, ids[i], err) 646 } 647 648 if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil { 649 return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subject.ID, err) 650 } 651 } 652 653 for i := range ids { 654 if err := repo.deleteByID(tx, ids[i]); err != nil { 655 return xerrors.Errorf("error in delete method(%d) [%v]: %w", i, ids[i], err) 656 } 657 } 658 659 return nil 660 } 661 662 func (repo *taskRepository) get(v interface{}, doc *firestore.DocumentRef, _ ...GetOption) (*Task, error) { 663 var ( 664 snapShot *firestore.DocumentSnapshot 665 err error 666 ) 667 668 switch x := v.(type) { 669 case *firestore.Transaction: 670 snapShot, err = x.Get(doc) 671 case context.Context: 672 snapShot, err = doc.Get(x) 673 default: 674 return nil, xerrors.Errorf("invalid type: %v", x) 675 } 676 677 if err != nil { 678 if status.Code(err) == codes.NotFound { 679 return nil, ErrNotFound 680 } 681 return nil, xerrors.Errorf("error in Get method: %w", err) 682 } 683 684 subject := new(Task) 685 if err := snapShot.DataTo(&subject); err != nil { 686 return nil, xerrors.Errorf("error in DataTo method: %w", err) 687 } 688 689 subject.ID = snapShot.Ref.ID 690 691 return subject, nil 692 } 693 694 func (repo *taskRepository) getMulti(v interface{}, ids []string, _ ...GetOption) ([]*Task, error) { 695 var ( 696 snapShots []*firestore.DocumentSnapshot 697 err error 698 collect = repo.GetCollection() 699 drs = make([]*firestore.DocumentRef, len(ids)) 700 ) 701 702 for i, id := range ids { 703 ref := collect.Doc(id) 704 drs[i] = ref 705 } 706 707 switch x := v.(type) { 708 case *firestore.Transaction: 709 snapShots, err = x.GetAll(drs) 710 case context.Context: 711 snapShots, err = repo.firestoreClient.GetAll(x, drs) 712 default: 713 return nil, xerrors.Errorf("invalid type: %v", v) 714 } 715 716 if err != nil { 717 return nil, xerrors.Errorf("error in GetAll method: %w", err) 718 } 719 720 subjects := make([]*Task, 0, len(ids)) 721 mErr := NewMultiErrors() 722 for i, snapShot := range snapShots { 723 if !snapShot.Exists() { 724 mErr = append(mErr, NewMultiError(i, ErrNotFound)) 725 continue 726 } 727 728 subject := new(Task) 729 if err = snapShot.DataTo(&subject); err != nil { 730 return nil, xerrors.Errorf("error in DataTo method: %w", err) 731 } 732 733 subject.ID = snapShot.Ref.ID 734 subjects = append(subjects, subject) 735 } 736 737 if len(mErr) == 0 { 738 return subjects, nil 739 } 740 741 return subjects, mErr 742 } 743 744 func (repo *taskRepository) insert(v interface{}, subject *Task) (string, error) { 745 var ( 746 dr = repo.GetCollection().NewDoc() 747 err error 748 ) 749 750 switch x := v.(type) { 751 case *firestore.Transaction: 752 err = x.Create(dr, subject) 753 case context.Context: 754 _, err = dr.Create(x, subject) 755 default: 756 return "", xerrors.Errorf("invalid type: %v", v) 757 } 758 759 if err != nil { 760 if status.Code(err) == codes.AlreadyExists { 761 return "", xerrors.Errorf("error in Create method: err=%+v: %w", err, ErrAlreadyExists) 762 } 763 return "", xerrors.Errorf("error in Create method: %w", err) 764 } 765 766 subject.ID = dr.ID 767 768 return dr.ID, nil 769 } 770 771 func (repo *taskRepository) update(v interface{}, subject *Task) error { 772 var ( 773 dr = repo.GetDocRef(subject.ID) 774 err error 775 ) 776 777 switch x := v.(type) { 778 case *firestore.Transaction: 779 err = x.Set(dr, subject) 780 case context.Context: 781 _, err = dr.Set(x, subject) 782 default: 783 return xerrors.Errorf("invalid type: %v", v) 784 } 785 786 if err != nil { 787 return xerrors.Errorf("error in Set method: %w", err) 788 } 789 790 return nil 791 } 792 793 func (repo *taskRepository) strictUpdate(v interface{}, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error { 794 var ( 795 dr = repo.GetDocRef(id) 796 err error 797 ) 798 799 updates := updater(Task{}, param) 800 801 switch x := v.(type) { 802 case *firestore.Transaction: 803 err = x.Update(dr, updates, opts...) 804 case context.Context: 805 _, err = dr.Update(x, updates, opts...) 806 default: 807 return xerrors.Errorf("invalid type: %v", v) 808 } 809 810 if err != nil { 811 return xerrors.Errorf("error in Update method: %w", err) 812 } 813 814 return nil 815 } 816 817 func (repo *taskRepository) deleteByID(v interface{}, id string) error { 818 dr := repo.GetDocRef(id) 819 var err error 820 821 switch x := v.(type) { 822 case *firestore.Transaction: 823 err = x.Delete(dr, firestore.Exists) 824 case context.Context: 825 _, err = dr.Delete(x, firestore.Exists) 826 default: 827 return xerrors.Errorf("invalid type: %v", v) 828 } 829 830 if err != nil { 831 return xerrors.Errorf("error in Delete method: %w", err) 832 } 833 834 return nil 835 } 836 837 func (repo *taskRepository) runQuery(v interface{}, query firestore.Query) ([]*Task, error) { 838 var iter *firestore.DocumentIterator 839 840 switch x := v.(type) { 841 case *firestore.Transaction: 842 iter = x.Documents(query) 843 case context.Context: 844 iter = query.Documents(x) 845 default: 846 return nil, xerrors.Errorf("invalid type: %v", v) 847 } 848 849 defer iter.Stop() 850 851 subjects := make([]*Task, 0) 852 853 for { 854 doc, err := iter.Next() 855 if err == iterator.Done { 856 break 857 } 858 if err != nil { 859 return nil, xerrors.Errorf("error in Next method: %w", err) 860 } 861 862 subject := new(Task) 863 864 if err = doc.DataTo(&subject); err != nil { 865 return nil, xerrors.Errorf("error in DataTo method: %w", err) 866 } 867 868 subject.ID = doc.Ref.ID 869 subjects = append(subjects, subject) 870 } 871 872 return subjects, nil 873 } 874 875 // BUG(54m): there may be potential bugs 876 func (repo *taskRepository) searchByParam(v interface{}, param *TaskSearchParam) ([]*Task, *PagingResult, error) { 877 query := func() firestore.Query { 878 return repo.GetCollection().Query 879 }() 880 filters := xim.NewFilters(&xim.Config{ 881 IgnoreCase: true, 882 SaveNoFiltersIndex: true, 883 }) 884 885 if param.ID != nil { 886 for _, chain := range param.ID.QueryGroup { 887 var value interface{} 888 switch val := chain.Value.(type) { 889 case string: 890 value = repo.GetDocRef(val) 891 case []string: 892 docRefs := make([]*firestore.DocumentRef, len(val)) 893 for i := range val { 894 docRefs[i] = repo.GetDocRef(val[i]) 895 } 896 value = docRefs 897 default: 898 return nil, nil, xerrors.Errorf("document id can only be of type `string` and `[]string`. value: %#v", chain.Value) 899 } 900 query = query.Where(firestore.DocumentID, chain.Operator, value) 901 } 902 if direction := param.ID.OrderByDirection; direction > 0 { 903 query = query.OrderBy(firestore.DocumentID, direction) 904 query = param.ID.BuildCursorQuery(query) 905 } 906 } 907 if param.Desc != nil { 908 for _, chain := range param.Desc.QueryGroup { 909 query = query.Where("description", chain.Operator, chain.Value) 910 } 911 if direction := param.Desc.OrderByDirection; direction > 0 { 912 query = query.OrderBy("description", direction) 913 query = param.Desc.BuildCursorQuery(query) 914 } 915 value, ok := param.Desc.Filter.Value.(string) 916 // The value of the "indexer" tag = "e,p,s,l" 917 // Add/AddBiunigrams/AddPrefix/AddSuffix is valid. 918 for _, filter := range param.Desc.Filter.FilterTypes { 919 switch filter { 920 case FilterTypeAddBiunigrams: 921 if ok { 922 filters.AddBiunigrams(TaskIndexLabelDescLike, value) 923 } 924 case FilterTypeAddPrefix: 925 if ok { 926 filters.AddPrefix(TaskIndexLabelDescPrefix, value) 927 } 928 case FilterTypeAddSuffix: 929 if ok { 930 filters.AddSuffix(TaskIndexLabelDescSuffix, value) 931 } 932 // Treat `Add` or otherwise as `Equal`. 933 case FilterTypeAdd: 934 fallthrough 935 default: 936 if !ok { 937 filters.AddSomething(TaskIndexLabelDescEqual, param.Desc.Filter.Value) 938 continue 939 } 940 filters.Add(TaskIndexLabelDescEqual, value) 941 } 942 } 943 } 944 if param.Created != nil { 945 for _, chain := range param.Created.QueryGroup { 946 query = query.Where("created", chain.Operator, chain.Value) 947 } 948 if direction := param.Created.OrderByDirection; direction > 0 { 949 query = query.OrderBy("created", direction) 950 query = param.Created.BuildCursorQuery(query) 951 } 952 value, ok := param.Created.Filter.Value.(string) 953 for _, filter := range param.Created.Filter.FilterTypes { 954 switch filter { 955 // Treat `Add` or otherwise as `Equal`. 956 case FilterTypeAdd: 957 fallthrough 958 default: 959 if !ok { 960 filters.AddSomething(TaskIndexLabelCreatedEqual, param.Created.Filter.Value) 961 continue 962 } 963 filters.Add(TaskIndexLabelCreatedEqual, value) 964 } 965 } 966 } 967 if param.Done != nil { 968 for _, chain := range param.Done.QueryGroup { 969 query = query.Where("done", chain.Operator, chain.Value) 970 } 971 if direction := param.Done.OrderByDirection; direction > 0 { 972 query = query.OrderBy("done", direction) 973 query = param.Done.BuildCursorQuery(query) 974 } 975 value, ok := param.Done.Filter.Value.(string) 976 for _, filter := range param.Done.Filter.FilterTypes { 977 switch filter { 978 // Treat `Add` or otherwise as `Equal`. 979 case FilterTypeAdd: 980 fallthrough 981 default: 982 if !ok { 983 filters.AddSomething(TaskIndexLabelDoneEqual, param.Done.Filter.Value) 984 continue 985 } 986 filters.Add(TaskIndexLabelDoneEqual, value) 987 } 988 } 989 } 990 if param.Done2 != nil { 991 for _, chain := range param.Done2.QueryGroup { 992 query = query.Where("done2", chain.Operator, chain.Value) 993 } 994 if direction := param.Done2.OrderByDirection; direction > 0 { 995 query = query.OrderBy("done2", direction) 996 query = param.Done2.BuildCursorQuery(query) 997 } 998 value, ok := param.Done2.Filter.Value.(string) 999 for _, filter := range param.Done2.Filter.FilterTypes { 1000 switch filter { 1001 // Treat `Add` or otherwise as `Equal`. 1002 case FilterTypeAdd: 1003 fallthrough 1004 default: 1005 if !ok { 1006 filters.AddSomething(TaskIndexLabelDone2Equal, param.Done2.Filter.Value) 1007 continue 1008 } 1009 filters.Add(TaskIndexLabelDone2Equal, value) 1010 } 1011 } 1012 } 1013 if param.Count != nil { 1014 for _, chain := range param.Count.QueryGroup { 1015 query = query.Where("count", chain.Operator, chain.Value) 1016 } 1017 if direction := param.Count.OrderByDirection; direction > 0 { 1018 query = query.OrderBy("count", direction) 1019 query = param.Count.BuildCursorQuery(query) 1020 } 1021 value, ok := param.Count.Filter.Value.(string) 1022 for _, filter := range param.Count.Filter.FilterTypes { 1023 switch filter { 1024 // Treat `Add` or otherwise as `Equal`. 1025 case FilterTypeAdd: 1026 fallthrough 1027 default: 1028 if !ok { 1029 filters.AddSomething(TaskIndexLabelCountEqual, param.Count.Filter.Value) 1030 continue 1031 } 1032 filters.Add(TaskIndexLabelCountEqual, value) 1033 } 1034 } 1035 } 1036 if param.Count64 != nil { 1037 for _, chain := range param.Count64.QueryGroup { 1038 query = query.Where("count64", chain.Operator, chain.Value) 1039 } 1040 if direction := param.Count64.OrderByDirection; direction > 0 { 1041 query = query.OrderBy("count64", direction) 1042 query = param.Count64.BuildCursorQuery(query) 1043 } 1044 value, ok := param.Count64.Filter.Value.(string) 1045 for _, filter := range param.Count64.Filter.FilterTypes { 1046 switch filter { 1047 // Treat `Add` or otherwise as `Equal`. 1048 case FilterTypeAdd: 1049 fallthrough 1050 default: 1051 if !ok { 1052 filters.AddSomething(TaskIndexLabelCount64Equal, param.Count64.Filter.Value) 1053 continue 1054 } 1055 filters.Add(TaskIndexLabelCount64Equal, value) 1056 } 1057 } 1058 } 1059 if param.NameList != nil { 1060 for _, chain := range param.NameList.QueryGroup { 1061 query = query.Where("nameList", chain.Operator, chain.Value) 1062 } 1063 } 1064 if param.Proportion != nil { 1065 for _, chain := range param.Proportion.QueryGroup { 1066 query = query.Where("proportion", chain.Operator, chain.Value) 1067 } 1068 if direction := param.Proportion.OrderByDirection; direction > 0 { 1069 query = query.OrderBy("proportion", direction) 1070 query = param.Proportion.BuildCursorQuery(query) 1071 } 1072 value, ok := param.Proportion.Filter.Value.(string) 1073 // The value of the "indexer" tag = "e" 1074 for _, filter := range param.Proportion.Filter.FilterTypes { 1075 switch filter { 1076 // Treat `Add` or otherwise as `Equal`. 1077 case FilterTypeAdd: 1078 fallthrough 1079 default: 1080 if !ok { 1081 filters.AddSomething(TaskIndexLabelProportionEqual, param.Proportion.Filter.Value) 1082 continue 1083 } 1084 filters.Add(TaskIndexLabelProportionEqual, value) 1085 } 1086 } 1087 } 1088 if param.Flag != nil { 1089 for _, chain := range param.Flag.QueryGroup { 1090 items, ok := chain.Value.(map[string]float64) 1091 if !ok { 1092 continue 1093 } 1094 for key, value := range items { 1095 query = query.WherePath(firestore.FieldPath{"flag", key}, chain.Operator, value) 1096 } 1097 } 1098 } 1099 if param.SliceSubTask != nil { 1100 for _, chain := range param.SliceSubTask.QueryGroup { 1101 query = query.Where("slice_sub_task", chain.Operator, chain.Value) 1102 } 1103 } 1104 1105 build, err := filters.Build() 1106 if err != nil { 1107 return nil, nil, xerrors.Errorf("failed to filter build: %w", err) 1108 } 1109 for key := range build { 1110 query = query.WherePath(firestore.FieldPath{"indexes", key}, OpTypeEqual, true) 1111 } 1112 1113 limit := param.CursorLimit + 1 1114 1115 if param.CursorKey != "" { 1116 var ( 1117 ds *firestore.DocumentSnapshot 1118 err error 1119 ) 1120 switch x := v.(type) { 1121 case *firestore.Transaction: 1122 ds, err = x.Get(repo.GetDocRef(param.CursorKey)) 1123 case context.Context: 1124 ds, err = repo.GetDocRef(param.CursorKey).Get(x) 1125 default: 1126 return nil, nil, xerrors.Errorf("invalid x type: %v", v) 1127 } 1128 if err != nil { 1129 if status.Code(err) == codes.NotFound { 1130 return nil, nil, ErrNotFound 1131 } 1132 return nil, nil, xerrors.Errorf("error in Get method: %w", err) 1133 } 1134 query = query.StartAt(ds) 1135 } 1136 1137 if limit > 1 { 1138 query = query.Limit(limit) 1139 } 1140 1141 subjects, err := repo.runQuery(v, query) 1142 if err != nil { 1143 return nil, nil, xerrors.Errorf("error in runQuery method: %w", err) 1144 } 1145 1146 pagingResult := &PagingResult{ 1147 Length: len(subjects), 1148 } 1149 if limit > 1 && limit == pagingResult.Length { 1150 next := pagingResult.Length - 1 1151 pagingResult.NextCursorKey = subjects[next].ID 1152 subjects = subjects[:next] 1153 pagingResult.Length-- 1154 } 1155 1156 return subjects, pagingResult, nil 1157 } 1158 1159 func (repo *taskRepository) search(v interface{}, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) { 1160 if (param == nil && q == nil) || (param != nil && q != nil) { 1161 return nil, xerrors.New("either one should be nil") 1162 } 1163 1164 query := func() firestore.Query { 1165 if q != nil { 1166 return *q 1167 } 1168 return repo.GetCollection().Query 1169 }() 1170 1171 if q == nil { 1172 subjects, _, err := repo.searchByParam(v, param) 1173 if err != nil { 1174 return nil, xerrors.Errorf("error in searchByParam method: %w", err) 1175 } 1176 1177 return subjects, nil 1178 } 1179 1180 return repo.runQuery(v, query) 1181 }