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 ¬{ 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 ¬Next{ 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 ¬Contains{ 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 }