github.com/cayleygraph/cayley@v0.7.7/graph/iterator/hasa.go (about) 1 // Copyright 2014 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package iterator 16 17 // Defines one of the base iterators, the HasA iterator. The HasA takes a 18 // subiterator of links, and acts as an iterator of nodes in the given 19 // direction. The name comes from the idea that a "link HasA subject" or a "link 20 // HasA predicate". 21 // 22 // HasA is weird in that it may return the same value twice if on the Next() 23 // path. That's okay -- in reality, it can be viewed as returning the value for 24 // a new quad, but to make logic much simpler, here we have the HasA. 25 // 26 // Likewise, it's important to think about Contains()ing a HasA. When given a 27 // value to check, it means "Check all predicates that have this value for your 28 // direction against the subiterator." This would imply that there's more than 29 // one possibility for the same Contains()ed value. While we could return the 30 // number of options, it's simpler to return one, and then call NextPath() 31 // enough times to enumerate the options. (In fact, one could argue that the 32 // raison d'etre for NextPath() is this iterator). 33 // 34 // Alternatively, can be seen as the dual of the LinksTo iterator. 35 36 import ( 37 "context" 38 "fmt" 39 40 "github.com/cayleygraph/cayley/clog" 41 "github.com/cayleygraph/cayley/graph" 42 "github.com/cayleygraph/quad" 43 ) 44 45 var _ graph.IteratorFuture = &HasA{} 46 47 // A HasA consists of a reference back to the graph.QuadStore that it references, 48 // a primary subiterator, a direction in which the quads for that subiterator point, 49 // and a temporary holder for the iterator generated on Contains(). 50 type HasA struct { 51 it *hasA 52 graph.Iterator 53 } 54 55 // Construct a new HasA iterator, given the quad subiterator, and the quad 56 // direction for which it stands. 57 func NewHasA(qs graph.QuadIndexer, subIt graph.Iterator, d quad.Direction) *HasA { 58 it := &HasA{ 59 it: newHasA(qs, graph.AsShape(subIt), d), 60 } 61 it.Iterator = graph.NewLegacy(it.it, it) 62 return it 63 } 64 65 func (it *HasA) AsShape() graph.IteratorShape { 66 it.Close() 67 return it.it 68 } 69 70 // Direction accessor. 71 func (it *HasA) Direction() quad.Direction { return it.it.Direction() } 72 73 var _ graph.IteratorShapeCompat = &hasA{} 74 75 // A HasA consists of a reference back to the graph.QuadStore that it references, 76 // a primary subiterator, a direction in which the quads for that subiterator point, 77 // and a temporary holder for the iterator generated on Contains(). 78 type hasA struct { 79 qs graph.QuadIndexer 80 primary graph.IteratorShape 81 dir quad.Direction 82 } 83 84 // Construct a new HasA iterator, given the quad subiterator, and the quad 85 // direction for which it stands. 86 func newHasA(qs graph.QuadIndexer, subIt graph.IteratorShape, d quad.Direction) *hasA { 87 return &hasA{ 88 qs: qs, 89 primary: subIt, 90 dir: d, 91 } 92 } 93 94 func (it *hasA) Iterate() graph.Scanner { 95 return newHasANext(it.qs, it.primary.Iterate(), it.dir) 96 } 97 98 func (it *hasA) Lookup() graph.Index { 99 return newHasAContains(it.qs, it.primary.Lookup(), it.dir) 100 } 101 102 func (it *hasA) AsLegacy() graph.Iterator { 103 it2 := &HasA{it: it} 104 it2.Iterator = graph.NewLegacy(it, it2) 105 return it2 106 } 107 108 // Return our sole subiterator. 109 func (it *hasA) SubIterators() []graph.IteratorShape { 110 return []graph.IteratorShape{it.primary} 111 } 112 113 // Direction accessor. 114 func (it *hasA) Direction() quad.Direction { return it.dir } 115 116 // Pass the Optimize() call along to the subiterator. If it becomes Null, 117 // then the HasA becomes Null (there are no quads that have any directions). 118 func (it *hasA) Optimize(ctx context.Context) (graph.IteratorShape, bool) { 119 newPrimary, changed := it.primary.Optimize(ctx) 120 if changed { 121 it.primary = newPrimary 122 if IsNull2(it.primary) { 123 return it.primary, true 124 } 125 } 126 return it, false 127 } 128 129 func (it *hasA) String() string { 130 return fmt.Sprintf("HasA(%v)", it.dir) 131 } 132 133 // GetStats() returns the statistics on the HasA iterator. This is curious. Next 134 // cost is easy, it's an extra call or so on top of the subiterator Next cost. 135 // ContainsCost involves going to the graph.QuadStore, iterating out values, and hoping 136 // one sticks -- potentially expensive, depending on fanout. Size, however, is 137 // potentially smaller. we know at worst it's the size of the subiterator, but 138 // if there are many repeated values, it could be much smaller in totality. 139 func (it *hasA) Stats(ctx context.Context) (graph.IteratorCosts, error) { 140 subitStats, err := it.primary.Stats(ctx) 141 // TODO(barakmich): These should really come from the quadstore itself 142 // and be optimized. 143 faninFactor := int64(1) 144 fanoutFactor := int64(30) 145 nextConstant := int64(2) 146 quadConstant := int64(1) 147 return graph.IteratorCosts{ 148 NextCost: quadConstant + subitStats.NextCost, 149 ContainsCost: (fanoutFactor * nextConstant) * subitStats.ContainsCost, 150 Size: graph.Size{ 151 Size: faninFactor * subitStats.Size.Size, 152 Exact: false, 153 }, 154 }, err 155 } 156 157 // A HasA consists of a reference back to the graph.QuadStore that it references, 158 // a primary subiterator, a direction in which the quads for that subiterator point, 159 // and a temporary holder for the iterator generated on Contains(). 160 type hasANext struct { 161 qs graph.QuadIndexer 162 primary graph.Scanner 163 dir quad.Direction 164 result graph.Ref 165 } 166 167 // Construct a new HasA iterator, given the quad subiterator, and the quad 168 // direction for which it stands. 169 func newHasANext(qs graph.QuadIndexer, subIt graph.Scanner, d quad.Direction) *hasANext { 170 return &hasANext{ 171 qs: qs, 172 primary: subIt, 173 dir: d, 174 } 175 } 176 177 // Direction accessor. 178 func (it *hasANext) Direction() quad.Direction { return it.dir } 179 180 // Pass the TagResults down the chain. 181 func (it *hasANext) TagResults(dst map[string]graph.Ref) { 182 it.primary.TagResults(dst) 183 } 184 185 func (it *hasANext) String() string { 186 return fmt.Sprintf("HasANext(%v)", it.dir) 187 } 188 189 // Get the next result that matches this branch. 190 func (it *hasANext) NextPath(ctx context.Context) bool { 191 return it.primary.NextPath(ctx) 192 } 193 194 // Next advances the iterator. This is simpler than Contains. We have a 195 // subiterator we can get a value from, and we can take that resultant quad, 196 // pull our direction out of it, and return that. 197 func (it *hasANext) Next(ctx context.Context) bool { 198 if !it.primary.Next(ctx) { 199 return false 200 } 201 it.result = it.qs.QuadDirection(it.primary.Result(), it.dir) 202 return true 203 } 204 205 func (it *hasANext) Err() error { 206 return it.primary.Err() 207 } 208 209 func (it *hasANext) Result() graph.Ref { 210 return it.result 211 } 212 213 // Close the subiterator, the result iterator (if any) and the HasA. It closes 214 // all subiterators it can, but returns the first error it encounters. 215 func (it *hasANext) Close() error { 216 return it.primary.Close() 217 } 218 219 // A HasA consists of a reference back to the graph.QuadStore that it references, 220 // a primary subiterator, a direction in which the quads for that subiterator point, 221 // and a temporary holder for the iterator generated on Contains(). 222 type hasAContains struct { 223 qs graph.QuadIndexer 224 primary graph.Index 225 dir quad.Direction 226 results graph.Scanner 227 result graph.Ref 228 err error 229 } 230 231 // Construct a new HasA iterator, given the quad subiterator, and the quad 232 // direction for which it stands. 233 func newHasAContains(qs graph.QuadIndexer, subIt graph.Index, d quad.Direction) graph.Index { 234 return &hasAContains{ 235 qs: qs, 236 primary: subIt, 237 dir: d, 238 } 239 } 240 241 // Direction accessor. 242 func (it *hasAContains) Direction() quad.Direction { return it.dir } 243 244 // Pass the TagResults down the chain. 245 func (it *hasAContains) TagResults(dst map[string]graph.Ref) { 246 it.primary.TagResults(dst) 247 } 248 249 func (it *hasAContains) String() string { 250 return fmt.Sprintf("HasAContains(%v)", it.dir) 251 } 252 253 // Check a value against our internal iterator. In order to do this, we must first open a new 254 // iterator of "quads that have `val` in our direction", given to us by the quad store, 255 // and then Next() values out of that iterator and Contains() them against our subiterator. 256 func (it *hasAContains) Contains(ctx context.Context, val graph.Ref) bool { 257 if clog.V(4) { 258 clog.Infof("Id is %v", val) 259 } 260 // TODO(barakmich): Optimize this 261 if it.results != nil { 262 it.results.Close() 263 } 264 it.results = graph.AsShape(it.qs.QuadIterator(it.dir, val)).Iterate() 265 ok := it.nextContains(ctx) 266 if it.err != nil { 267 return false 268 } 269 return ok 270 } 271 272 // nextContains() is shared code between Contains() and GetNextResult() -- calls next on the 273 // result iterator (a quad iterator based on the last checked value) and returns true if 274 // another match is made. 275 func (it *hasAContains) nextContains(ctx context.Context) bool { 276 if it.results == nil { 277 return false 278 } 279 for it.results.Next(ctx) { 280 link := it.results.Result() 281 if clog.V(4) { 282 clog.Infof("Quad is %v", it.qs.Quad(link)) 283 } 284 if it.primary.Contains(ctx, link) { 285 it.result = it.qs.QuadDirection(link, it.dir) 286 return true 287 } 288 } 289 it.err = it.results.Err() 290 return false 291 } 292 293 // Get the next result that matches this branch. 294 func (it *hasAContains) NextPath(ctx context.Context) bool { 295 // Order here is important. If the subiterator has a NextPath, then we 296 // need do nothing -- there is a next result, and we shouldn't move forward. 297 // However, we then need to get the next result from our last Contains(). 298 // 299 // The upshot is, the end of NextPath() bubbles up from the bottom of the 300 // iterator tree up, and we need to respect that. 301 if clog.V(4) { 302 clog.Infof("HASA %p NextPath", it) 303 } 304 if it.primary.NextPath(ctx) { 305 return true 306 } 307 it.err = it.primary.Err() 308 if it.err != nil { 309 return false 310 } 311 312 result := it.nextContains(ctx) // Sets it.err if there's an error 313 if it.err != nil { 314 return false 315 } 316 if clog.V(4) { 317 clog.Infof("HASA %p NextPath Returns %v", it, result) 318 } 319 return result 320 } 321 322 func (it *hasAContains) Err() error { 323 return it.err 324 } 325 326 func (it *hasAContains) Result() graph.Ref { 327 return it.result 328 } 329 330 // Close the subiterator, the result iterator (if any) and the HasA. It closes 331 // all subiterators it can, but returns the first error it encounters. 332 func (it *hasAContains) Close() error { 333 err := it.primary.Close() 334 if it.results != nil { 335 if err2 := it.results.Close(); err2 != nil && err == nil { 336 err = err2 337 } 338 } 339 return err 340 }