github.com/MontFerret/ferret@v0.18.0/pkg/runtime/collections/sort.go (about)

     1  package collections
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/MontFerret/ferret/pkg/runtime/core"
     9  )
    10  
    11  type (
    12  	SortDirection int
    13  
    14  	Comparator func(ctx context.Context, first, second *core.Scope) (int64, error)
    15  
    16  	Sorter struct {
    17  		fn        Comparator
    18  		direction SortDirection
    19  	}
    20  
    21  	SortIterator struct {
    22  		values  Iterator
    23  		sorters []*Sorter
    24  		ready   bool
    25  		result  []*core.Scope
    26  		pos     int
    27  	}
    28  )
    29  
    30  const (
    31  	SortDirectionAsc  SortDirection = 1
    32  	SortDirectionDesc SortDirection = -1
    33  )
    34  
    35  func SortDirectionFromString(str string) SortDirection {
    36  	if strings.EqualFold(str, "DESC") {
    37  		return SortDirectionDesc
    38  	}
    39  
    40  	return SortDirectionAsc
    41  }
    42  
    43  func IsValidSortDirection(direction SortDirection) bool {
    44  	switch direction {
    45  	case SortDirectionAsc, SortDirectionDesc:
    46  		return true
    47  	default:
    48  		return false
    49  	}
    50  }
    51  
    52  func NewSorter(fn Comparator, direction SortDirection) (*Sorter, error) {
    53  	if fn == nil {
    54  		return nil, core.Error(core.ErrMissedArgument, "fn")
    55  	}
    56  
    57  	if !IsValidSortDirection(direction) {
    58  		return nil, core.Error(core.ErrInvalidArgument, "direction")
    59  	}
    60  
    61  	return &Sorter{fn, direction}, nil
    62  }
    63  
    64  func NewSortIterator(
    65  	values Iterator,
    66  	comparators ...*Sorter,
    67  ) (*SortIterator, error) {
    68  	if values == nil {
    69  		return nil, core.Error(core.ErrMissedArgument, "values")
    70  	}
    71  
    72  	if len(comparators) == 0 {
    73  		return nil, core.Error(core.ErrMissedArgument, "comparator")
    74  	}
    75  
    76  	return &SortIterator{
    77  		values,
    78  		comparators,
    79  		false,
    80  		nil,
    81  		0,
    82  	}, nil
    83  }
    84  
    85  func (iterator *SortIterator) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) {
    86  	// we need to initialize the iterator
    87  	if !iterator.ready {
    88  		iterator.ready = true
    89  		sorted, err := iterator.sort(ctx, scope)
    90  
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  
    95  		iterator.result = sorted
    96  	}
    97  
    98  	if len(iterator.result) > iterator.pos {
    99  		idx := iterator.pos
   100  		val := iterator.result[idx]
   101  
   102  		iterator.pos++
   103  
   104  		return val, nil
   105  	}
   106  
   107  	return nil, core.ErrNoMoreData
   108  }
   109  
   110  func (iterator *SortIterator) sort(ctx context.Context, scope *core.Scope) ([]*core.Scope, error) {
   111  	scopes, err := ToSlice(ctx, scope, iterator.values)
   112  
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	var failure error
   118  
   119  	sort.SliceStable(scopes, func(i, j int) bool {
   120  		// ignore next execution
   121  		if failure != nil {
   122  			return false
   123  		}
   124  
   125  		var out bool
   126  
   127  		for _, comp := range iterator.sorters {
   128  			left := scopes[i]
   129  			right := scopes[j]
   130  
   131  			eq, err := comp.fn(ctx, left, right)
   132  
   133  			if err != nil {
   134  				failure = err
   135  				out = false
   136  
   137  				break
   138  			}
   139  
   140  			eq *= int64(comp.direction)
   141  
   142  			if eq == -1 {
   143  				out = true
   144  				break
   145  			}
   146  
   147  			if eq == 1 {
   148  				out = false
   149  				break
   150  			}
   151  		}
   152  
   153  		return out
   154  	})
   155  
   156  	if failure != nil {
   157  		return nil, failure
   158  	}
   159  
   160  	return scopes, nil
   161  }