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 }}