github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/collection/collection.go (about)

     1  /*
     2   * Copyright (c) 2021-present unTill Pro, Ltd.
     3  *
     4  * @author Michael Saigachenko
     5  */
     6  
     7  package collection
     8  
     9  import (
    10  	"github.com/voedger/voedger/pkg/goutils/iterate"
    11  
    12  	"github.com/voedger/voedger/pkg/appdef"
    13  	"github.com/voedger/voedger/pkg/istructs"
    14  	"github.com/voedger/voedger/pkg/state"
    15  )
    16  
    17  type kbAndID struct {
    18  	istructs.IStateKeyBuilder
    19  	id    istructs.RecordID
    20  	isNew bool
    21  }
    22  
    23  func collectionProjector(appDef appdef.IAppDef) istructs.Projector {
    24  	return istructs.Projector{
    25  		Name: QNameProjectorCollection,
    26  		Func: func(event istructs.IPLogEvent, s istructs.IState, intents istructs.IIntents) (err error) {
    27  			is := &idService{
    28  				state: s,
    29  				cache: make(map[istructs.RecordID]istructs.IRecord),
    30  			}
    31  
    32  			newKey := func(docQname appdef.QName, docID, elementID istructs.RecordID) (kb istructs.IStateKeyBuilder, err error) {
    33  				kb, err = s.KeyBuilder(state.View, QNameCollectionView)
    34  				if err != nil {
    35  					// notest
    36  					return
    37  				}
    38  				kb.PutInt32(Field_PartKey, PartitionKeyCollection)
    39  				kb.PutQName(Field_DocQName, docQname)
    40  				kb.PutRecordID(field_DocID, docID)
    41  				kb.PutRecordID(field_ElementID, elementID)
    42  				return
    43  			}
    44  
    45  			apply := func(kb istructs.IStateKeyBuilder, record istructs.IRecord, isNew bool) (err error) {
    46  				if !isNew {
    47  					sv, ok, err := s.CanExist(kb)
    48  					if err != nil {
    49  						// notest
    50  						return err
    51  					}
    52  					if ok && sv.AsInt64(state.ColOffset) >= int64(event.WLogOffset()) {
    53  						// skip for idempotency
    54  						return nil
    55  					}
    56  				}
    57  				vb, err := intents.NewValue(kb)
    58  				if err != nil {
    59  					// notest
    60  					return err
    61  				}
    62  				vb.PutInt64(state.ColOffset, int64(event.WLogOffset()))
    63  				vb.PutRecord(Field_Record, record)
    64  				return nil
    65  			}
    66  
    67  			keyBuildersAndIDs := []kbAndID{}
    68  			err = iterate.ForEachError(event.CUDs, func(rec istructs.ICUDRow) error {
    69  				kind := appDef.Type(rec.QName()).Kind()
    70  				if kind != appdef.TypeKind_CDoc && kind != appdef.TypeKind_CRecord {
    71  					return nil
    72  				}
    73  				kb, err := is.state.KeyBuilder(state.Record, appdef.NullQName)
    74  				if err != nil {
    75  					// notest
    76  					return err
    77  				}
    78  				kb.PutRecordID(state.Field_ID, rec.ID())
    79  				keyBuildersAndIDs = append(keyBuildersAndIDs, kbAndID{
    80  					IStateKeyBuilder: kb,
    81  					id:               rec.ID(),
    82  					isNew:            rec.IsNew(),
    83  				})
    84  				return nil
    85  			})
    86  			if err != nil {
    87  				// notest
    88  				return err
    89  			}
    90  
    91  			keyBuilders := make([]istructs.IStateKeyBuilder, len(keyBuildersAndIDs))
    92  			for i, kbID := range keyBuildersAndIDs {
    93  				keyBuilders[i] = kbID.IStateKeyBuilder
    94  			}
    95  			err = is.state.MustExistAll(keyBuilders, func(key istructs.IKeyBuilder, sv istructs.IStateValue, ok bool) (err error) {
    96  				record := sv.AsRecord("")
    97  				is.cache[record.ID()] = record
    98  				return nil
    99  			})
   100  			if err != nil {
   101  				return err
   102  			}
   103  			for _, kbAndID := range keyBuildersAndIDs {
   104  				record := is.cache[kbAndID.id]
   105  				root, err := is.findRootByID(record.ID())
   106  				if err != nil {
   107  					return err
   108  				}
   109  				elementID := record.ID()
   110  				if record.ID() == root.ID() {
   111  					elementID = istructs.NullRecordID
   112  				}
   113  				kb, err := newKey(root.QName(), root.ID(), elementID)
   114  				if err != nil {
   115  					// notest
   116  					return err
   117  				}
   118  				if err = apply(kb, record, kbAndID.isNew); err != nil {
   119  					return err
   120  				}
   121  			}
   122  			return nil
   123  		},
   124  	}
   125  }
   126  
   127  type idService struct {
   128  	state istructs.IState
   129  	cache map[istructs.RecordID]istructs.IRecord
   130  }
   131  
   132  func (s *idService) findRecordByID(id istructs.RecordID) (record istructs.IRecord, err error) {
   133  	kb, err := s.state.KeyBuilder(state.Record, appdef.NullQName)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	kb.PutRecordID(state.Field_ID, id)
   138  
   139  	sv, err := s.state.MustExist(kb)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	return sv.AsRecord(""), nil
   144  }
   145  
   146  func (s *idService) findRootByID(id istructs.RecordID) (root istructs.IRecord, err error) {
   147  	rec := s.cache[id]
   148  	if rec == nil {
   149  		if rec, err = s.findRecordByID(id); err != nil {
   150  			return nil, err
   151  		}
   152  		s.cache[id] = rec
   153  	}
   154  	if rec.Parent() == istructs.NullRecordID {
   155  		return rec, nil
   156  	}
   157  	return s.findRootByID(rec.Parent())
   158  }