github.com/mithrandie/csvq@v1.18.1/lib/query/sort_value.go (about)

     1  package query
     2  
     3  import (
     4  	"bytes"
     5  	"math"
     6  	"strings"
     7  
     8  	"github.com/mithrandie/csvq/lib/option"
     9  	"github.com/mithrandie/csvq/lib/parser"
    10  	"github.com/mithrandie/csvq/lib/value"
    11  
    12  	"github.com/mithrandie/ternary"
    13  )
    14  
    15  type SortValueType int
    16  
    17  const (
    18  	NullType SortValueType = iota
    19  	IntegerType
    20  	FloatType
    21  	DatetimeType
    22  	BooleanType
    23  	StringType
    24  )
    25  
    26  type SortValues []*SortValue
    27  
    28  func (values SortValues) Less(compareValues SortValues, directions []int, nullPositions []int) bool {
    29  	for i, val := range values {
    30  		t := val.Less(compareValues[i])
    31  		if t != ternary.UNKNOWN {
    32  			if directions[i] == parser.ASC {
    33  				return t == ternary.TRUE
    34  			} else {
    35  				return t == ternary.FALSE
    36  			}
    37  		}
    38  
    39  		if val.Type == NullType && compareValues[i].Type != NullType {
    40  			if nullPositions[i] == parser.FIRST {
    41  				return true
    42  			} else {
    43  				return false
    44  			}
    45  		}
    46  		if val.Type != NullType && compareValues[i].Type == NullType {
    47  			if nullPositions[i] == parser.FIRST {
    48  				return false
    49  			} else {
    50  				return true
    51  			}
    52  		}
    53  	}
    54  	return false
    55  }
    56  
    57  func (values SortValues) EquivalentTo(compareValues SortValues) bool {
    58  	if compareValues == nil {
    59  		return false
    60  	}
    61  
    62  	for i, val := range values {
    63  		if !val.EquivalentTo(compareValues[i]) {
    64  			return false
    65  		}
    66  	}
    67  	return true
    68  }
    69  
    70  func (values SortValues) Serialize(buf *bytes.Buffer) {
    71  	for i, val := range values {
    72  		if 0 < i {
    73  			buf.WriteByte(58)
    74  		}
    75  
    76  		if val.SerializedKey != nil {
    77  			buf.Write(val.SerializedKey.Bytes())
    78  			continue
    79  		}
    80  
    81  		switch val.Type {
    82  		case NullType:
    83  			serializeNull(buf)
    84  		case IntegerType, BooleanType:
    85  			serializeInteger(buf, value.Int64ToStr(val.Integer))
    86  		case FloatType:
    87  			serializeFloat(buf, value.Float64ToStr(val.Float, false))
    88  		case DatetimeType:
    89  			serializeDatetimeFromUnixNano(buf, val.Datetime)
    90  		case StringType:
    91  			serializeString(buf, val.String)
    92  		}
    93  	}
    94  }
    95  
    96  type SortValue struct {
    97  	Type SortValueType
    98  
    99  	SerializedKey *bytes.Buffer
   100  
   101  	Integer  int64
   102  	Float    float64
   103  	Datetime int64
   104  	String   string
   105  }
   106  
   107  func NewSortValue(val value.Primary, flags *option.Flags) *SortValue {
   108  	sortValue := &SortValue{}
   109  
   110  	if value.IsNull(val) {
   111  		sortValue.Type = NullType
   112  	} else if i := value.ToIntegerStrictly(val); !value.IsNull(i) {
   113  		s := value.ToString(val)
   114  		sortValue.Type = IntegerType
   115  		sortValue.Integer = i.(*value.Integer).Raw()
   116  		sortValue.Float = float64(sortValue.Integer)
   117  		sortValue.String = strings.ToUpper(option.TrimSpace(s.(*value.String).Raw()))
   118  		value.Discard(i)
   119  		value.Discard(s)
   120  	} else if f := value.ToFloat(val); !value.IsNull(f) {
   121  		s := value.ToString(val)
   122  		sortValue.Type = FloatType
   123  		sortValue.Float = f.(*value.Float).Raw()
   124  		sortValue.String = strings.ToUpper(option.TrimSpace(s.(*value.String).Raw()))
   125  		value.Discard(f)
   126  		value.Discard(s)
   127  	} else if dt := value.ToDatetime(val, flags.DatetimeFormat, flags.GetTimeLocation()); !value.IsNull(dt) {
   128  		t := dt.(*value.Datetime).Raw()
   129  		sortValue.Type = DatetimeType
   130  		sortValue.Datetime = t.UnixNano()
   131  		value.Discard(dt)
   132  	} else if b := value.ToBoolean(val); !value.IsNull(b) {
   133  		sortValue.Type = BooleanType
   134  		if b.(*value.Boolean).Raw() {
   135  			sortValue.Integer = 1
   136  		} else {
   137  			sortValue.Integer = 0
   138  		}
   139  	} else if s, ok := val.(*value.String); ok {
   140  		sortValue.Type = StringType
   141  		sortValue.String = strings.ToUpper(option.TrimSpace(s.Raw()))
   142  	} else {
   143  		sortValue.Type = NullType
   144  	}
   145  
   146  	if flags.StrictEqual {
   147  		sortValue.SerializedKey = &bytes.Buffer{}
   148  		SerializeIdenticalKey(sortValue.SerializedKey, val)
   149  	}
   150  
   151  	return sortValue
   152  }
   153  
   154  func (v *SortValue) Less(compareValue *SortValue) ternary.Value {
   155  	if v.SerializedKey != nil {
   156  		if bytes.Equal(v.SerializedKey.Bytes(), compareValue.SerializedKey.Bytes()) {
   157  			return ternary.UNKNOWN
   158  		}
   159  
   160  		if v.SerializedKey.Bytes()[1] == 83 && compareValue.SerializedKey.Bytes()[1] == 83 {
   161  			return ternary.ConvertFromBool(v.String < compareValue.String)
   162  		}
   163  	}
   164  
   165  	switch v.Type {
   166  	case IntegerType:
   167  		switch compareValue.Type {
   168  		case IntegerType:
   169  			if v.Integer == compareValue.Integer {
   170  				return ternary.UNKNOWN
   171  			}
   172  			return ternary.ConvertFromBool(v.Integer < compareValue.Integer)
   173  		case FloatType:
   174  			return ternary.ConvertFromBool(v.Float < compareValue.Float)
   175  		case StringType:
   176  			return ternary.ConvertFromBool(v.String < compareValue.String)
   177  		}
   178  	case FloatType:
   179  		switch compareValue.Type {
   180  		case IntegerType, FloatType:
   181  			if math.IsNaN(v.Float) || math.IsNaN(compareValue.Float) {
   182  				if math.IsNaN(v.Float) && math.IsNaN(compareValue.Float) {
   183  					return ternary.UNKNOWN
   184  				}
   185  
   186  				if math.IsNaN(v.Float) {
   187  					return ternary.FALSE
   188  				}
   189  
   190  				// math.IsNaN(compareValue.Float)
   191  				return ternary.TRUE
   192  			}
   193  
   194  			if v.Float == compareValue.Float {
   195  				return ternary.UNKNOWN
   196  			}
   197  
   198  			return ternary.ConvertFromBool(v.Float < compareValue.Float)
   199  		case StringType:
   200  			return ternary.ConvertFromBool(v.String < compareValue.String)
   201  		}
   202  	case DatetimeType:
   203  		switch compareValue.Type {
   204  		case DatetimeType:
   205  			if v.Datetime == compareValue.Datetime {
   206  				return ternary.UNKNOWN
   207  			}
   208  			return ternary.ConvertFromBool(v.Datetime < compareValue.Datetime)
   209  		}
   210  	case StringType:
   211  		switch compareValue.Type {
   212  		case IntegerType, FloatType, StringType:
   213  			if v.String == compareValue.String {
   214  				return ternary.UNKNOWN
   215  			}
   216  			return ternary.ConvertFromBool(v.String < compareValue.String)
   217  		}
   218  	}
   219  
   220  	return ternary.UNKNOWN
   221  }
   222  
   223  func (v *SortValue) EquivalentTo(compareValue *SortValue) bool {
   224  	if v.SerializedKey != nil {
   225  		return bytes.Equal(v.SerializedKey.Bytes(), compareValue.SerializedKey.Bytes())
   226  	}
   227  
   228  	switch v.Type {
   229  	case IntegerType:
   230  		switch compareValue.Type {
   231  		case IntegerType, BooleanType:
   232  			return v.Integer == compareValue.Integer
   233  		}
   234  	case FloatType:
   235  		switch compareValue.Type {
   236  		case FloatType:
   237  			if math.IsNaN(v.Float) && math.IsNaN(compareValue.Float) {
   238  				return true
   239  			}
   240  			return v.Float == compareValue.Float
   241  		}
   242  	case DatetimeType:
   243  		switch compareValue.Type {
   244  		case DatetimeType:
   245  			return v.Datetime == compareValue.Datetime
   246  		}
   247  	case BooleanType:
   248  		switch compareValue.Type {
   249  		case BooleanType, IntegerType:
   250  			return v.Integer == compareValue.Integer
   251  		}
   252  	case StringType:
   253  		switch compareValue.Type {
   254  		case StringType:
   255  			return v.String == compareValue.String
   256  		}
   257  	case NullType:
   258  		return compareValue.Type == NullType
   259  	}
   260  
   261  	return false
   262  }