github.com/cayleygraph/cayley@v0.7.7/graph/iterator/sort.go (about)

     1  package iterator
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  
     7  	"github.com/cayleygraph/cayley/graph"
     8  )
     9  
    10  var _ graph.IteratorFuture = &Sort{}
    11  
    12  // Sort iterator orders values from it's subiterator.
    13  type Sort struct {
    14  	it *sortIt
    15  	graph.Iterator
    16  }
    17  
    18  // NewSort creates a new Sort iterator.
    19  // TODO(dennwc): This iterator must not be used inside And: it may be moved to a Contains branch and won't do anything.
    20  //               We should make And/Intersect account for this.
    21  func NewSort(namer graph.Namer, it graph.Iterator) *Sort {
    22  	return &Sort{
    23  		it: newSort(namer, graph.AsShape(it)),
    24  	}
    25  }
    26  
    27  // AsShape returns Sort's underlying iterator shape
    28  func (it *Sort) AsShape() graph.IteratorShape {
    29  	return it.it
    30  }
    31  
    32  type sortIt struct {
    33  	namer graph.Namer
    34  	subIt graph.IteratorShape
    35  }
    36  
    37  var _ graph.IteratorShapeCompat = (*sortIt)(nil)
    38  
    39  func newSort(namer graph.Namer, subIt graph.IteratorShape) *sortIt {
    40  	return &sortIt{namer, subIt}
    41  }
    42  
    43  func (it *sortIt) Iterate() graph.Scanner {
    44  	return newSortNext(it.namer, it.subIt.Iterate())
    45  }
    46  
    47  func (it *sortIt) AsLegacy() graph.Iterator {
    48  	it2 := &Sort{it: it}
    49  	it2.Iterator = graph.NewLegacy(it, it2)
    50  	return it2
    51  }
    52  
    53  func (it *sortIt) Lookup() graph.Index {
    54  	// TODO(dennwc): Lookup doesn't need any sorting. Using it this way is a bug in the optimizer.
    55  	//               But instead of failing here, let still allow the query to execute. It won't be sorted,
    56  	//               but it will work at least. Later consider changing returning an error here.
    57  	return it.subIt.Lookup()
    58  }
    59  
    60  func (it *sortIt) Optimize(ctx context.Context) (graph.IteratorShape, bool) {
    61  	newIt, optimized := it.subIt.Optimize(ctx)
    62  	if optimized {
    63  		it.subIt = newIt
    64  	}
    65  	return it, false
    66  }
    67  
    68  func (it *sortIt) Stats(ctx context.Context) (graph.IteratorCosts, error) {
    69  	subStats, err := it.subIt.Stats(ctx)
    70  	return graph.IteratorCosts{
    71  		// TODO(dennwc): better cost calculation; we probably need an InitCost defined in graph.IteratorCosts
    72  		NextCost:     subStats.NextCost * 2,
    73  		ContainsCost: subStats.ContainsCost,
    74  		Size: graph.Size{
    75  			Size:  subStats.Size.Size,
    76  			Exact: true,
    77  		},
    78  	}, err
    79  }
    80  
    81  func (it *sortIt) String() string {
    82  	return "Sort"
    83  }
    84  
    85  // SubIterators returns a slice of the sub iterators.
    86  func (it *sortIt) SubIterators() []graph.IteratorShape {
    87  	return []graph.IteratorShape{it.subIt}
    88  }
    89  
    90  type sortValue struct {
    91  	result
    92  	str   string
    93  	paths []result
    94  }
    95  type sortByString []sortValue
    96  
    97  func (v sortByString) Len() int { return len(v) }
    98  func (v sortByString) Less(i, j int) bool {
    99  	return v[i].str < v[j].str
   100  }
   101  func (v sortByString) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
   102  
   103  type sortNext struct {
   104  	namer     graph.Namer
   105  	subIt     graph.Scanner
   106  	ordered   sortByString
   107  	result    result
   108  	err       error
   109  	index     int
   110  	pathIndex int
   111  }
   112  
   113  func newSortNext(namer graph.Namer, subIt graph.Scanner) *sortNext {
   114  	return &sortNext{
   115  		namer:     namer,
   116  		subIt:     subIt,
   117  		pathIndex: -1,
   118  	}
   119  }
   120  
   121  func (it *sortNext) TagResults(dst map[string]graph.Value) {
   122  	for tag, value := range it.result.tags {
   123  		dst[tag] = value
   124  	}
   125  }
   126  
   127  func (it *sortNext) Err() error {
   128  	return it.err
   129  }
   130  
   131  func (it *sortNext) Result() graph.Value {
   132  	return it.result.id
   133  }
   134  
   135  func (it *sortNext) Next(ctx context.Context) bool {
   136  	if it.err != nil {
   137  		return false
   138  	}
   139  	if it.ordered == nil {
   140  		v, err := getSortedValues(ctx, it.namer, it.subIt)
   141  		it.ordered = v
   142  		it.err = err
   143  		if it.err != nil {
   144  			return false
   145  		}
   146  	}
   147  	if it.index >= len(it.ordered) {
   148  		return false
   149  	}
   150  	it.pathIndex = -1
   151  	it.result = it.ordered[it.index].result
   152  	it.index++
   153  	return true
   154  }
   155  
   156  func (it *sortNext) NextPath(ctx context.Context) bool {
   157  	if it.index >= len(it.ordered) {
   158  		return false
   159  	}
   160  	r := it.ordered[it.index]
   161  	if it.pathIndex+1 >= len(r.paths) {
   162  		return false
   163  	}
   164  	it.pathIndex++
   165  	it.result = r.paths[it.pathIndex]
   166  	return true
   167  }
   168  
   169  func (it *sortNext) Close() error {
   170  	it.ordered = nil
   171  	return it.subIt.Close()
   172  }
   173  
   174  func (it *sortNext) String() string {
   175  	return "SortNext"
   176  }
   177  
   178  func getSortedValues(ctx context.Context, namer graph.Namer, it graph.Scanner) (sortByString, error) {
   179  	var v sortByString
   180  	for it.Next(ctx) {
   181  		id := it.Result()
   182  		// TODO(dennwc): batch and use graph.ValuesOf
   183  		name := namer.NameOf(id)
   184  		str := name.String()
   185  		tags := make(map[string]graph.Ref)
   186  		it.TagResults(tags)
   187  		val := sortValue{
   188  			result: result{id, tags},
   189  			str:    str,
   190  		}
   191  		for it.NextPath(ctx) {
   192  			tags = make(map[string]graph.Ref)
   193  			it.TagResults(tags)
   194  			val.paths = append(val.paths, result{id, tags})
   195  		}
   196  		v = append(v, val)
   197  	}
   198  	if err := it.Err(); err != nil {
   199  		return v, err
   200  	}
   201  	sort.Sort(v)
   202  	return v, nil
   203  }