github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/databases/orm/orm_querym2m.go (about)

     1  // The original package is migrated from beego and modified, you can find orignal from following link:
     2  //    "github.com/beego/beego/"
     3  //
     4  // Copyright 2023 IAC. All Rights Reserved.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //      http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package orm
    19  
    20  import (
    21  	"context"
    22  	"reflect"
    23  )
    24  
    25  // model to model struct
    26  type queryM2M struct {
    27  	md  interface{}
    28  	mi  *modelInfo
    29  	fi  *fieldInfo
    30  	qs  *querySet
    31  	ind reflect.Value
    32  }
    33  
    34  // add models to origin models when creating queryM2M.
    35  // example:
    36  //
    37  //		m2m := orm.QueryM2M(post,"Tag")
    38  //		m2m.Add(&Tag1{},&Tag2{})
    39  //	 for _,tag := range post.Tags{}
    40  //
    41  // make sure the relation is defined in post model struct tag.
    42  func (o *queryM2M) Add(mds ...interface{}) (int64, error) {
    43  	return o.AddWithCtx(context.Background(), mds...)
    44  }
    45  
    46  func (o *queryM2M) AddWithCtx(ctx context.Context, mds ...interface{}) (int64, error) {
    47  	fi := o.fi
    48  	mi := fi.relThroughModelInfo
    49  	mfi := fi.reverseFieldInfo
    50  	rfi := fi.reverseFieldInfoTwo
    51  
    52  	orm := o.qs.orm
    53  	dbase := orm.alias.DbBaser
    54  
    55  	var models []interface{}
    56  	var otherValues []interface{}
    57  	var otherNames []string
    58  
    59  	for _, colname := range mi.fields.dbcols {
    60  		if colname != mfi.column && colname != rfi.column && colname != fi.mi.fields.pk.column &&
    61  			mi.fields.columns[colname] != mi.fields.pk {
    62  			otherNames = append(otherNames, colname)
    63  		}
    64  	}
    65  	for i, md := range mds {
    66  		if reflect.Indirect(reflect.ValueOf(md)).Kind() != reflect.Struct && i > 0 {
    67  			otherValues = append(otherValues, md)
    68  			mds = append(mds[:i], mds[i+1:]...)
    69  		}
    70  	}
    71  	for _, md := range mds {
    72  		val := reflect.ValueOf(md)
    73  		if val.Kind() == reflect.Slice || val.Kind() == reflect.Array {
    74  			for i := 0; i < val.Len(); i++ {
    75  				v := val.Index(i)
    76  				if v.CanInterface() {
    77  					models = append(models, v.Interface())
    78  				}
    79  			}
    80  		} else {
    81  			models = append(models, md)
    82  		}
    83  	}
    84  
    85  	_, v1, exist := getExistPk(o.mi, o.ind)
    86  	if !exist {
    87  		panic(ErrMissPK)
    88  	}
    89  
    90  	names := []string{mfi.column, rfi.column}
    91  
    92  	values := make([]interface{}, 0, len(models)*2)
    93  	for _, md := range models {
    94  
    95  		ind := reflect.Indirect(reflect.ValueOf(md))
    96  		var v2 interface{}
    97  		if ind.Kind() != reflect.Struct {
    98  			v2 = ind.Interface()
    99  		} else {
   100  			_, v2, exist = getExistPk(fi.relModelInfo, ind)
   101  			if !exist {
   102  				panic(ErrMissPK)
   103  			}
   104  		}
   105  		values = append(values, v1, v2)
   106  
   107  	}
   108  	names = append(names, otherNames...)
   109  	values = append(values, otherValues...)
   110  	return dbase.InsertValue(ctx, orm.db, mi, true, names, values)
   111  }
   112  
   113  // remove models following the origin model relationship
   114  func (o *queryM2M) Remove(mds ...interface{}) (int64, error) {
   115  	return o.RemoveWithCtx(context.Background(), mds...)
   116  }
   117  
   118  func (o *queryM2M) RemoveWithCtx(ctx context.Context, mds ...interface{}) (int64, error) {
   119  	fi := o.fi
   120  	qs := o.qs.Filter(fi.reverseFieldInfo.name, o.md)
   121  
   122  	return qs.Filter(fi.reverseFieldInfoTwo.name+ExprSep+"in", mds).Delete()
   123  }
   124  
   125  // check model is existed in relationship of origin model
   126  func (o *queryM2M) Exist(md interface{}) bool {
   127  	return o.ExistWithCtx(context.Background(), md)
   128  }
   129  
   130  func (o *queryM2M) ExistWithCtx(ctx context.Context, md interface{}) bool {
   131  	fi := o.fi
   132  	return o.qs.Filter(fi.reverseFieldInfo.name, o.md).
   133  		Filter(fi.reverseFieldInfoTwo.name, md).ExistWithCtx(ctx)
   134  }
   135  
   136  // clean all models in related of origin model
   137  func (o *queryM2M) Clear() (int64, error) {
   138  	return o.ClearWithCtx(context.Background())
   139  }
   140  
   141  func (o *queryM2M) ClearWithCtx(ctx context.Context) (int64, error) {
   142  	fi := o.fi
   143  	return o.qs.Filter(fi.reverseFieldInfo.name, o.md).DeleteWithCtx(ctx)
   144  }
   145  
   146  // count all related models of origin model
   147  func (o *queryM2M) Count() (int64, error) {
   148  	return o.CountWithCtx(context.Background())
   149  }
   150  
   151  func (o *queryM2M) CountWithCtx(ctx context.Context) (int64, error) {
   152  	fi := o.fi
   153  	return o.qs.Filter(fi.reverseFieldInfo.name, o.md).CountWithCtx(ctx)
   154  }
   155  
   156  var _ QueryM2Mer = new(queryM2M)
   157  
   158  // create new M2M queryer.
   159  func newQueryM2M(md interface{}, o *ormBase, mi *modelInfo, fi *fieldInfo, ind reflect.Value) QueryM2Mer {
   160  	qm2m := new(queryM2M)
   161  	qm2m.md = md
   162  	qm2m.mi = mi
   163  	qm2m.fi = fi
   164  	qm2m.ind = ind
   165  	qm2m.qs = newQuerySet(o, fi.relThroughModelInfo).(*querySet)
   166  	return qm2m
   167  }