github.com/cayleygraph/cayley@v0.7.7/graph/iterator/linksto.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 LinksTo iterator. A LinksTo takes a 18 // subiterator of nodes, and contains an iteration of links which "link to" 19 // those nodes in a given direction. 20 // 21 // Next()ing a LinksTo is straightforward -- iterate through all links to // 22 // things in the subiterator, and then advance the subiterator, and do it again. 23 // LinksTo is therefore sensitive to growing with a fanout. (A small-sized 24 // subiterator could cause LinksTo to be large). 25 // 26 // Contains()ing a LinksTo means, given a link, take the direction we care about 27 // and check if it's in our subiterator. Checking is therefore fairly cheap, and 28 // similar to checking the subiterator alone. 29 // 30 // Can be seen as the dual of the HasA iterator. 31 32 import ( 33 "context" 34 "fmt" 35 36 "github.com/cayleygraph/cayley/graph" 37 "github.com/cayleygraph/quad" 38 ) 39 40 var _ graph.IteratorFuture = &LinksTo{} 41 42 // A LinksTo has a reference back to the graph.QuadStore (to create the iterators 43 // for each node) the subiterator, and the direction the iterator comes from. 44 // `next_it` is the tempoarary iterator held per result in `primary_it`. 45 type LinksTo struct { 46 it *linksTo 47 graph.Iterator 48 } 49 50 // Construct a new LinksTo iterator around a direction and a subiterator of 51 // nodes. 52 func NewLinksTo(qs graph.QuadIndexer, sub graph.Iterator, d quad.Direction) *LinksTo { 53 it := &LinksTo{ 54 it: newLinksTo(qs, graph.AsShape(sub), d), 55 } 56 it.Iterator = graph.NewLegacy(it.it, it) 57 return it 58 } 59 60 func (it *LinksTo) AsShape() graph.IteratorShape { 61 it.Close() 62 return it.it 63 } 64 65 // Return the direction under consideration. 66 func (it *LinksTo) Direction() quad.Direction { return it.it.Direction() } 67 68 var _ graph.IteratorShapeCompat = &linksTo{} 69 70 // A LinksTo has a reference back to the graph.QuadStore (to create the iterators 71 // for each node) the subiterator, and the direction the iterator comes from. 72 // `next_it` is the tempoarary iterator held per result in `primary_it`. 73 type linksTo struct { 74 qs graph.QuadIndexer 75 primary graph.IteratorShape 76 dir quad.Direction 77 size graph.Size 78 } 79 80 // Construct a new LinksTo iterator around a direction and a subiterator of 81 // nodes. 82 func newLinksTo(qs graph.QuadIndexer, it graph.IteratorShape, d quad.Direction) *linksTo { 83 return &linksTo{ 84 qs: qs, 85 primary: it, 86 dir: d, 87 } 88 } 89 90 // Return the direction under consideration. 91 func (it *linksTo) Direction() quad.Direction { return it.dir } 92 93 func (it *linksTo) Iterate() graph.Scanner { 94 return newLinksToNext(it.qs, it.primary.Iterate(), it.dir) 95 } 96 97 func (it *linksTo) Lookup() graph.Index { 98 return newLinksToContains(it.qs, it.primary.Lookup(), it.dir) 99 } 100 101 func (it *linksTo) AsLegacy() graph.Iterator { 102 it2 := &LinksTo{it: it} 103 it2.Iterator = graph.NewLegacy(it, it2) 104 return it2 105 } 106 107 func (it *linksTo) String() string { 108 return fmt.Sprintf("LinksTo(%v)", it.dir) 109 } 110 111 // Return a list containing only our subiterator. 112 func (it *linksTo) SubIterators() []graph.IteratorShape { 113 return []graph.IteratorShape{it.primary} 114 } 115 116 // Optimize the LinksTo, by replacing it if it can be. 117 func (it *linksTo) Optimize(ctx context.Context) (graph.IteratorShape, bool) { 118 newPrimary, changed := it.primary.Optimize(ctx) 119 if changed { 120 it.primary = newPrimary 121 if IsNull2(it.primary) { 122 return it.primary, true 123 } 124 } 125 return it, false 126 } 127 128 // Return a guess as to how big or costly it is to next the iterator. 129 func (it *linksTo) Stats(ctx context.Context) (graph.IteratorCosts, error) { 130 subitStats, err := it.primary.Stats(ctx) 131 // TODO(barakmich): These should really come from the quadstore itself 132 checkConstant := int64(1) 133 nextConstant := int64(2) 134 return graph.IteratorCosts{ 135 NextCost: nextConstant + subitStats.NextCost, 136 ContainsCost: checkConstant + subitStats.ContainsCost, 137 Size: it.getSize(ctx), 138 }, err 139 } 140 141 func (it *linksTo) getSize(ctx context.Context) graph.Size { 142 if it.size.Size != 0 { 143 return it.size 144 } 145 if fixed, ok := graph.AsLegacy(it.primary).(*Fixed); ok { 146 // get real sizes from sub iterators 147 var ( 148 sz int64 149 exact = true 150 ) 151 for _, v := range fixed.Values() { 152 sit := it.qs.QuadIterator(it.dir, v) 153 n, ex := sit.Size() 154 sit.Close() 155 sz += n 156 exact = exact && ex 157 } 158 it.size.Size, it.size.Exact = sz, exact 159 return it.size 160 } 161 // TODO(barakmich): It should really come from the quadstore itself 162 const fanoutFactor = 20 163 st, _ := it.primary.Stats(ctx) 164 st.Size.Size *= fanoutFactor 165 it.size.Size, it.size.Exact = st.Size.Size, false 166 return it.size 167 } 168 169 // A LinksTo has a reference back to the graph.QuadStore (to create the iterators 170 // for each node) the subiterator, and the direction the iterator comes from. 171 // `next_it` is the tempoarary iterator held per result in `primary_it`. 172 type linksToNext struct { 173 qs graph.QuadIndexer 174 primary graph.Scanner 175 dir quad.Direction 176 nextIt graph.Scanner 177 result graph.Ref 178 err error 179 } 180 181 // Construct a new LinksTo iterator around a direction and a subiterator of 182 // nodes. 183 func newLinksToNext(qs graph.QuadIndexer, it graph.Scanner, d quad.Direction) graph.Scanner { 184 return &linksToNext{ 185 qs: qs, 186 primary: it, 187 dir: d, 188 nextIt: newNull().Iterate(), 189 } 190 } 191 192 // Return the direction under consideration. 193 func (it *linksToNext) Direction() quad.Direction { return it.dir } 194 195 // Tag these results, and our subiterator's results. 196 func (it *linksToNext) TagResults(dst map[string]graph.Ref) { 197 it.primary.TagResults(dst) 198 } 199 200 func (it *linksToNext) String() string { 201 return fmt.Sprintf("LinksToNext(%v)", it.dir) 202 } 203 204 // Next()ing a LinksTo operates as described above. 205 func (it *linksToNext) Next(ctx context.Context) bool { 206 for { 207 if it.nextIt.Next(ctx) { 208 it.result = it.nextIt.Result() 209 return true 210 } 211 212 // If there's an error in the 'next' iterator, we save it and we're done. 213 it.err = it.nextIt.Err() 214 if it.err != nil { 215 return false 216 } 217 218 // Subiterator is empty, get another one 219 if !it.primary.Next(ctx) { 220 // Possibly save error 221 it.err = it.primary.Err() 222 223 // We're out of nodes in our subiterator, so we're done as well. 224 return false 225 } 226 it.nextIt.Close() 227 it.nextIt = it.qs.QuadIterator(it.dir, it.primary.Result()) 228 229 // Continue -- return the first in the next set. 230 } 231 } 232 233 func (it *linksToNext) Err() error { 234 return it.err 235 } 236 237 func (it *linksToNext) Result() graph.Ref { 238 return it.result 239 } 240 241 // Close closes the iterator. It closes all subiterators it can, but 242 // returns the first error it encounters. 243 func (it *linksToNext) Close() error { 244 err := it.nextIt.Close() 245 246 _err := it.primary.Close() 247 if _err != nil && err == nil { 248 err = _err 249 } 250 251 return err 252 } 253 254 // We won't ever have a new result, but our subiterators might. 255 func (it *linksToNext) NextPath(ctx context.Context) bool { 256 ok := it.primary.NextPath(ctx) 257 if !ok { 258 it.err = it.primary.Err() 259 } 260 return ok 261 } 262 263 // A LinksTo has a reference back to the graph.QuadStore (to create the iterators 264 // for each node) the subiterator, and the direction the iterator comes from. 265 // `next_it` is the tempoarary iterator held per result in `primary_it`. 266 type linksToContains struct { 267 qs graph.QuadIndexer 268 primary graph.Index 269 dir quad.Direction 270 result graph.Ref 271 } 272 273 // Construct a new LinksTo iterator around a direction and a subiterator of 274 // nodes. 275 func newLinksToContains(qs graph.QuadIndexer, it graph.Index, d quad.Direction) graph.Index { 276 return &linksToContains{ 277 qs: qs, 278 primary: it, 279 dir: d, 280 } 281 } 282 283 // Return the direction under consideration. 284 func (it *linksToContains) Direction() quad.Direction { return it.dir } 285 286 // Tag these results, and our subiterator's results. 287 func (it *linksToContains) TagResults(dst map[string]graph.Ref) { 288 it.primary.TagResults(dst) 289 } 290 291 func (it *linksToContains) String() string { 292 return fmt.Sprintf("LinksToContains(%v)", it.dir) 293 } 294 295 // If it checks in the right direction for the subiterator, it is a valid link 296 // for the LinksTo. 297 func (it *linksToContains) Contains(ctx context.Context, val graph.Ref) bool { 298 node := it.qs.QuadDirection(val, it.dir) 299 if it.primary.Contains(ctx, node) { 300 it.result = val 301 return true 302 } 303 return false 304 } 305 306 func (it *linksToContains) Err() error { 307 return it.primary.Err() 308 } 309 310 func (it *linksToContains) Result() graph.Ref { 311 return it.result 312 } 313 314 // Close closes the iterator. It closes all subiterators it can, but 315 // returns the first error it encounters. 316 func (it *linksToContains) Close() error { 317 return it.primary.Close() 318 } 319 320 // We won't ever have a new result, but our subiterators might. 321 func (it *linksToContains) NextPath(ctx context.Context) bool { 322 return it.primary.NextPath(ctx) 323 }