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

     1  package iterator
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/cayleygraph/cayley/graph"
     7  )
     8  
     9  var _ graph.IteratorFuture = &Unique{}
    10  
    11  // Unique iterator removes duplicate values from it's subiterator.
    12  type Unique struct {
    13  	it *unique
    14  	graph.Iterator
    15  }
    16  
    17  func NewUnique(subIt graph.Iterator) *Unique {
    18  	it := &Unique{
    19  		it: newUnique(graph.AsShape(subIt)),
    20  	}
    21  	it.Iterator = graph.NewLegacy(it.it, it)
    22  	return it
    23  }
    24  
    25  func (it *Unique) AsShape() graph.IteratorShape {
    26  	it.Close()
    27  	return it.it
    28  }
    29  
    30  var _ graph.IteratorShapeCompat = (*unique)(nil)
    31  
    32  // Unique iterator removes duplicate values from it's subiterator.
    33  type unique struct {
    34  	subIt graph.IteratorShape
    35  }
    36  
    37  func newUnique(subIt graph.IteratorShape) *unique {
    38  	return &unique{
    39  		subIt: subIt,
    40  	}
    41  }
    42  
    43  func (it *unique) Iterate() graph.Scanner {
    44  	return newUniqueNext(it.subIt.Iterate())
    45  }
    46  
    47  func (it *unique) Lookup() graph.Index {
    48  	return newUniqueContains(it.subIt.Lookup())
    49  }
    50  
    51  func (it *unique) AsLegacy() graph.Iterator {
    52  	it2 := &Unique{it: it}
    53  	it2.Iterator = graph.NewLegacy(it, it2)
    54  	return it2
    55  }
    56  
    57  // SubIterators returns a slice of the sub iterators. The first iterator is the
    58  // primary iterator, for which the complement is generated.
    59  func (it *unique) SubIterators() []graph.IteratorShape {
    60  	return []graph.IteratorShape{it.subIt}
    61  }
    62  
    63  func (it *unique) Optimize(ctx context.Context) (graph.IteratorShape, bool) {
    64  	newIt, optimized := it.subIt.Optimize(ctx)
    65  	if optimized {
    66  		it.subIt = newIt
    67  	}
    68  	return it, false
    69  }
    70  
    71  const uniquenessFactor = 2
    72  
    73  func (it *unique) Stats(ctx context.Context) (graph.IteratorCosts, error) {
    74  	subStats, err := it.subIt.Stats(ctx)
    75  	return graph.IteratorCosts{
    76  		NextCost:     subStats.NextCost * uniquenessFactor,
    77  		ContainsCost: subStats.ContainsCost,
    78  		Size: graph.Size{
    79  			Size:  subStats.Size.Size / uniquenessFactor,
    80  			Exact: false,
    81  		},
    82  	}, err
    83  }
    84  
    85  func (it *unique) String() string {
    86  	return "Unique"
    87  }
    88  
    89  // Unique iterator removes duplicate values from it's subiterator.
    90  type uniqueNext struct {
    91  	subIt  graph.Scanner
    92  	result graph.Ref
    93  	err    error
    94  	seen   map[interface{}]bool
    95  }
    96  
    97  func newUniqueNext(subIt graph.Scanner) *uniqueNext {
    98  	return &uniqueNext{
    99  		subIt: subIt,
   100  		seen:  make(map[interface{}]bool),
   101  	}
   102  }
   103  
   104  func (it *uniqueNext) TagResults(dst map[string]graph.Ref) {
   105  	if it.subIt != nil {
   106  		it.subIt.TagResults(dst)
   107  	}
   108  }
   109  
   110  // Next advances the subiterator, continuing until it returns a value which it
   111  // has not previously seen.
   112  func (it *uniqueNext) Next(ctx context.Context) bool {
   113  	for it.subIt.Next(ctx) {
   114  		curr := it.subIt.Result()
   115  		key := graph.ToKey(curr)
   116  		if ok := it.seen[key]; !ok {
   117  			it.result = curr
   118  			it.seen[key] = true
   119  			return true
   120  		}
   121  	}
   122  	it.err = it.subIt.Err()
   123  	return false
   124  }
   125  
   126  func (it *uniqueNext) Err() error {
   127  	return it.err
   128  }
   129  
   130  func (it *uniqueNext) Result() graph.Ref {
   131  	return it.result
   132  }
   133  
   134  // NextPath for unique always returns false. If we were to return multiple
   135  // paths, we'd no longer be a unique result, so we have to choose only the first
   136  // path that got us here. Unique is serious on this point.
   137  func (it *uniqueNext) NextPath(ctx context.Context) bool {
   138  	return false
   139  }
   140  
   141  // Close closes the primary iterators.
   142  func (it *uniqueNext) Close() error {
   143  	it.seen = nil
   144  	return it.subIt.Close()
   145  }
   146  
   147  func (it *uniqueNext) String() string {
   148  	return "UniqueNext"
   149  }
   150  
   151  // Unique iterator removes duplicate values from it's subiterator.
   152  type uniqueContains struct {
   153  	subIt graph.Index
   154  }
   155  
   156  func newUniqueContains(subIt graph.Index) *uniqueContains {
   157  	return &uniqueContains{
   158  		subIt: subIt,
   159  	}
   160  }
   161  
   162  func (it *uniqueContains) TagResults(dst map[string]graph.Ref) {
   163  	if it.subIt != nil {
   164  		it.subIt.TagResults(dst)
   165  	}
   166  }
   167  
   168  func (it *uniqueContains) Err() error {
   169  	return it.subIt.Err()
   170  }
   171  
   172  func (it *uniqueContains) Result() graph.Ref {
   173  	return it.subIt.Result()
   174  }
   175  
   176  // Contains checks whether the passed value is part of the primary iterator,
   177  // which is irrelevant for uniqueness.
   178  func (it *uniqueContains) Contains(ctx context.Context, val graph.Ref) bool {
   179  	return it.subIt.Contains(ctx, val)
   180  }
   181  
   182  // NextPath for unique always returns false. If we were to return multiple
   183  // paths, we'd no longer be a unique result, so we have to choose only the first
   184  // path that got us here. Unique is serious on this point.
   185  func (it *uniqueContains) NextPath(ctx context.Context) bool {
   186  	return false
   187  }
   188  
   189  // Close closes the primary iterators.
   190  func (it *uniqueContains) Close() error {
   191  	return it.subIt.Close()
   192  }
   193  
   194  func (it *uniqueContains) String() string {
   195  	return "UniqueContains"
   196  }