github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/pkg/service/combine.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	consts "github.com/easysoft/zendata/internal/pkg/const"
     8  	"github.com/easysoft/zendata/internal/pkg/domain"
     9  	"github.com/easysoft/zendata/internal/pkg/helper"
    10  	stringUtils "github.com/easysoft/zendata/pkg/utils/string"
    11  	"github.com/easysoft/zendata/pkg/utils/vari"
    12  )
    13  
    14  type CombineService struct {
    15  	ExcelService       *ExcelService       `inject:""`
    16  	ExpressionService  *ExpressionService  `inject:""`
    17  	LoopService        *LoopService        `inject:""`
    18  	OutputService      *OutputService      `inject:""`
    19  	PlaceholderService *PlaceholderService `inject:""`
    20  }
    21  
    22  func (s *CombineService) CombineChildrenIfNeeded(field *domain.DefField, isOnTopLevel bool) {
    23  	if len(field.Fields) == 0 {
    24  		return
    25  	}
    26  
    27  	// 1. get values for child fields
    28  	if len(field.Values) == 0 {
    29  		for index, child := range field.Fields {
    30  			if len(child.Fields) > 0 && len(child.Values) == 0 { // no need to do if already generated
    31  				s.CombineChildrenIfNeeded(&(field.Fields[index]), false)
    32  			}
    33  
    34  			// for text output only
    35  			vari.GlobalVars.FieldNameToValuesMap[field.Fields[index].Field] = field.Fields[index].Values
    36  			vari.GlobalVars.FieldNameToFieldMap[field.Fields[index].Field] = field.Fields[index]
    37  		}
    38  	}
    39  
    40  	if !field.Join {
    41  		return
    42  	}
    43  
    44  	// 2. deal with expression
    45  	arrByField := make([][]interface{}, 0) // 2 dimension arr for child, [ [a,b,c], [1,2,3] ]
    46  	for i, child := range field.Fields {
    47  		if child.Value != "" {
    48  			vari.GlobalVars.FieldNameToValuesMap[child.Field] = s.ExpressionService.GenExpressionValues(child)
    49  		}
    50  
    51  		// select from excel with expr
    52  		if helper.IsSelectExcelWithExpr(child) {
    53  			vari.GlobalVars.FieldNameToValuesMap[child.Field] = s.ExcelService.genExcelValuesWithExpr(&child, vari.GlobalVars.FieldNameToValuesMap)
    54  		}
    55  
    56  		arrByField = append(arrByField, vari.GlobalVars.FieldNameToValuesMap[child.Field])
    57  
    58  		// clear child values after combined
    59  		field.Fields[i].Values = nil
    60  	}
    61  
    62  	// 3. get combined values for parent field
    63  	isRecursive := vari.GlobalVars.Recursive
    64  	if stringUtils.InArray(field.Mode, consts.Modes) { // set on field level
    65  		isRecursive = field.Mode == consts.ModeRecursive || field.Mode == consts.ModeRecursiveShort
    66  	}
    67  
    68  	if len(field.Values) == 0 && field.Fields != nil {
    69  		field.Values = s.combineChildrenValues(arrByField, isRecursive, isOnTopLevel)
    70  	}
    71  
    72  	s.LoopService.LoopAndFixFieldValues(field, true)
    73  }
    74  
    75  func (s *CombineService) combineChildrenValues(arrByField [][]interface{}, isRecursive, isOnTopLevel bool) (ret []interface{}) {
    76  	arrByRow := s.populateRowsFromTwoDimArr(arrByField, isRecursive, isOnTopLevel)
    77  
    78  	for _, arr := range arrByRow {
    79  		line := s.ConnectValues(arr)
    80  		ret = append(ret, line)
    81  	}
    82  
    83  	return
    84  }
    85  
    86  func (s *CombineService) populateRowsFromTwoDimArr(arrOfArr [][]interface{}, isRecursive, isOnTopLevel bool) (
    87  	values [][]interface{}) {
    88  	count := vari.GlobalVars.Total
    89  
    90  	if !isOnTopLevel {
    91  		if isRecursive {
    92  			count = s.getRecordCountForRecursive(arrOfArr)
    93  		} else {
    94  			count = s.getRecordCountForParallel(arrOfArr)
    95  		}
    96  	}
    97  
    98  	indexArr := make([]int, 0)
    99  	if isRecursive {
   100  		indexArr = s.getModArrForChildrenRecursive(arrOfArr)
   101  	}
   102  
   103  	for i := 0; i < count; i++ {
   104  		strArr := make([]interface{}, 0)
   105  		for j := 0; j < len(arrOfArr); j++ {
   106  			child := arrOfArr[j]
   107  			if len(child) == 0 {
   108  				continue
   109  			}
   110  
   111  			var index int
   112  			if isRecursive {
   113  				mod := indexArr[j]
   114  				index = i / mod % len(child)
   115  			} else {
   116  				index = i % len(child)
   117  			}
   118  
   119  			val := child[index]
   120  			strArr = append(strArr, val)
   121  		}
   122  
   123  		values = append(values, strArr)
   124  	}
   125  
   126  	return
   127  }
   128  
   129  func (s *CombineService) getRecordCountForParallel(arrOfArr [][]interface{}) int {
   130  	// get max count of 2nd dim arr
   131  	count := 1
   132  	for _, arr := range arrOfArr {
   133  		if count < len(arr) {
   134  			count = len(arr)
   135  		}
   136  	}
   137  
   138  	if count > vari.GlobalVars.Total {
   139  		count = vari.GlobalVars.Total
   140  	}
   141  
   142  	return count
   143  }
   144  
   145  func (s *CombineService) getRecordCountForRecursive(arrOfArr [][]interface{}) int {
   146  	count := 1
   147  	for i := 0; i < len(arrOfArr); i++ {
   148  		arr := arrOfArr[i]
   149  		count = len(arr) * count
   150  	}
   151  	return count
   152  }
   153  
   154  func (s *CombineService) getModArrForChildrenRecursive(arrOfArr [][]interface{}) []int {
   155  	indexArr := make([]int, 0)
   156  	for range arrOfArr {
   157  		indexArr = append(indexArr, 0)
   158  	}
   159  
   160  	for i := 0; i < len(arrOfArr); i++ {
   161  		loop := 1
   162  		for j := i + 1; j < len(arrOfArr); j++ {
   163  			loop = loop * len(arrOfArr[j])
   164  		}
   165  
   166  		indexArr[i] = loop
   167  	}
   168  
   169  	return indexArr
   170  }
   171  
   172  func (s *CombineService) ConnectValues(values []interface{}) (ret string) {
   173  	for i, item := range values {
   174  		col := fmt.Sprintf("%v", item)
   175  
   176  		if i > 0 && vari.GlobalVars.Human { // use a tab
   177  			ret = strings.TrimRight(ret, "\t")
   178  			col = strings.TrimLeft(col, "\t")
   179  
   180  			ret += "\t" + col
   181  		} else {
   182  			ret += col
   183  		}
   184  	}
   185  
   186  	return
   187  }