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

     1  package iterator
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/cayleygraph/cayley/graph"
     7  )
     8  
     9  var _ graph.IteratorFuture = &Not{}
    10  
    11  // Not iterator acts like a complement for the primary iterator.
    12  // It will return all the vertices which are not part of the primary iterator.
    13  type Not struct {
    14  	it *not
    15  	graph.Iterator
    16  }
    17  
    18  func NewNot(primaryIt, allIt graph.Iterator) *Not {
    19  	it := &Not{
    20  		it: newNot(graph.AsShape(primaryIt), graph.AsShape(allIt)),
    21  	}
    22  	it.Iterator = graph.NewLegacy(it.it, it)
    23  	return it
    24  }
    25  
    26  func (it *Not) AsShape() graph.IteratorShape {
    27  	it.Close()
    28  	return it.it
    29  }
    30  
    31  var _ graph.IteratorShapeCompat = (*not)(nil)
    32  
    33  // Not iterator acts like a complement for the primary iterator.
    34  // It will return all the vertices which are not part of the primary iterator.
    35  type not struct {
    36  	primary graph.IteratorShape
    37  	allIt   graph.IteratorShape
    38  }
    39  
    40  func newNot(primaryIt, allIt graph.IteratorShape) *not {
    41  	return &not{
    42  		primary: primaryIt,
    43  		allIt:   allIt,
    44  	}
    45  }
    46  
    47  func (it *not) Iterate() graph.Scanner {
    48  	return newNotNext(it.primary.Lookup(), it.allIt.Iterate())
    49  }
    50  
    51  func (it *not) Lookup() graph.Index {
    52  	return newNotContains(it.primary.Lookup())
    53  }
    54  
    55  func (it *not) AsLegacy() graph.Iterator {
    56  	it2 := &Not{it: it}
    57  	it2.Iterator = graph.NewLegacy(it, it2)
    58  	return it2
    59  }
    60  
    61  // SubIterators returns a slice of the sub iterators.
    62  // The first iterator is the primary iterator, for which the complement
    63  // is generated.
    64  func (it *not) SubIterators() []graph.IteratorShape {
    65  	return []graph.IteratorShape{it.primary, it.allIt}
    66  }
    67  
    68  func (it *not) Optimize(ctx context.Context) (graph.IteratorShape, bool) {
    69  	// TODO - consider wrapping the primary with a MaterializeIt
    70  	optimizedPrimaryIt, optimized := it.primary.Optimize(ctx)
    71  	if optimized {
    72  		it.primary = optimizedPrimaryIt
    73  	}
    74  	it.primary = newMaterialize(it.primary)
    75  	return it, false
    76  }
    77  
    78  func (it *not) Stats(ctx context.Context) (graph.IteratorCosts, error) {
    79  	primaryStats, err := it.primary.Stats(ctx)
    80  	allStats, err2 := it.allIt.Stats(ctx)
    81  	if err == nil {
    82  		err = err2
    83  	}
    84  	return graph.IteratorCosts{
    85  		NextCost:     allStats.NextCost + primaryStats.ContainsCost,
    86  		ContainsCost: primaryStats.ContainsCost,
    87  		Size: graph.Size{
    88  			Size:  allStats.Size.Size - primaryStats.Size.Size,
    89  			Exact: false,
    90  		},
    91  	}, err
    92  }
    93  
    94  func (it *not) String() string {
    95  	return "Not"
    96  }
    97  
    98  // Not iterator acts like a complement for the primary iterator.
    99  // It will return all the vertices which are not part of the primary iterator.
   100  type notNext struct {
   101  	primaryIt graph.Index
   102  	allIt     graph.Scanner
   103  	result    graph.Ref
   104  }
   105  
   106  func newNotNext(primaryIt graph.Index, allIt graph.Scanner) *notNext {
   107  	return &notNext{
   108  		primaryIt: primaryIt,
   109  		allIt:     allIt,
   110  	}
   111  }
   112  
   113  func (it *notNext) TagResults(dst map[string]graph.Ref) {
   114  	if it.primaryIt != nil {
   115  		it.primaryIt.TagResults(dst)
   116  	}
   117  }
   118  
   119  // Next advances the Not iterator. It returns whether there is another valid
   120  // new value. It fetches the next value of the all iterator which is not
   121  // contained by the primary iterator.
   122  func (it *notNext) Next(ctx context.Context) bool {
   123  	for it.allIt.Next(ctx) {
   124  		if curr := it.allIt.Result(); !it.primaryIt.Contains(ctx, curr) {
   125  			it.result = curr
   126  			return true
   127  		}
   128  	}
   129  	return false
   130  }
   131  
   132  func (it *notNext) Err() error {
   133  	if err := it.allIt.Err(); err != nil {
   134  		return err
   135  	}
   136  	if err := it.primaryIt.Err(); err != nil {
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  func (it *notNext) Result() graph.Ref {
   143  	return it.result
   144  }
   145  
   146  // NextPath checks whether there is another path. Not applicable, hence it will
   147  // return false.
   148  func (it *notNext) NextPath(ctx context.Context) bool {
   149  	return false
   150  }
   151  
   152  // Close closes the primary and all iterators.  It closes all subiterators
   153  // it can, but returns the first error it encounters.
   154  func (it *notNext) Close() error {
   155  	err := it.primaryIt.Close()
   156  	if err2 := it.allIt.Close(); err2 != nil && err == nil {
   157  		err = err2
   158  	}
   159  	return err
   160  }
   161  
   162  func (it *notNext) String() string {
   163  	return "NotNext"
   164  }
   165  
   166  // Not iterator acts like a complement for the primary iterator.
   167  // It will return all the vertices which are not part of the primary iterator.
   168  type notContains struct {
   169  	primaryIt graph.Index
   170  	result    graph.Ref
   171  	err       error
   172  }
   173  
   174  func newNotContains(primaryIt graph.Index) *notContains {
   175  	return &notContains{
   176  		primaryIt: primaryIt,
   177  	}
   178  }
   179  
   180  func (it *notContains) TagResults(dst map[string]graph.Ref) {
   181  	if it.primaryIt != nil {
   182  		it.primaryIt.TagResults(dst)
   183  	}
   184  }
   185  
   186  func (it *notContains) Err() error {
   187  	return it.err
   188  }
   189  
   190  func (it *notContains) Result() graph.Ref {
   191  	return it.result
   192  }
   193  
   194  // Contains checks whether the passed value is part of the primary iterator's
   195  // complement. For a valid value, it updates the Result returned by the iterator
   196  // to the value itself.
   197  func (it *notContains) Contains(ctx context.Context, val graph.Ref) bool {
   198  	if it.primaryIt.Contains(ctx, val) {
   199  		return false
   200  	}
   201  	it.err = it.primaryIt.Err()
   202  	if it.err != nil {
   203  		// Explicitly return 'false', since an error occurred.
   204  		return false
   205  	}
   206  	it.result = val
   207  	return true
   208  }
   209  
   210  // NextPath checks whether there is another path. Not applicable, hence it will
   211  // return false.
   212  func (it *notContains) NextPath(ctx context.Context) bool {
   213  	return false
   214  }
   215  
   216  // Close closes the primary and all iterators.  It closes all subiterators
   217  // it can, but returns the first error it encounters.
   218  func (it *notContains) Close() error {
   219  	return it.primaryIt.Close()
   220  }
   221  
   222  func (it *notContains) String() string {
   223  	return "NotContains"
   224  }