// Copyright 2014 The Cayley Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package iterator // Defines one of the base iterators, the LinksTo iterator. A LinksTo takes a // subiterator of nodes, and contains an iteration of links which "link to" // those nodes in a given direction. // // Next()ing a LinksTo is straightforward -- iterate through all links to // // things in the subiterator, and then advance the subiterator, and do it again. // LinksTo is therefore sensitive to growing with a fanout. (A small-sized // subiterator could cause LinksTo to be large). // // Check()ing a LinksTo means, given a link, take the direction we care about // and check if it's in our subiterator. Checking is therefore fairly cheap, and // similar to checking the subiterator alone. // // Can be seen as the dual of the HasA iterator. import ( "fmt" "strings" "github.com/google/cayley/graph" "github.com/google/cayley/quad" ) // A LinksTo has a reference back to the graph.TripleStore (to create the iterators // for each node) the subiterator, and the direction the iterator comes from. // `next_it` is the tempoarary iterator held per result in `primary_it`. type LinksTo struct { Base ts graph.TripleStore primaryIt graph.Iterator dir quad.Direction nextIt graph.Iterator } // Construct a new LinksTo iterator around a direction and a subiterator of // nodes. func NewLinksTo(ts graph.TripleStore, it graph.Iterator, d quad.Direction) *LinksTo { var lto LinksTo BaseInit(<o.Base) lto.ts = ts lto.primaryIt = it lto.dir = d lto.nextIt = &Null{} return <o } func (it *LinksTo) Reset() { it.primaryIt.Reset() if it.nextIt != nil { it.nextIt.Close() } it.nextIt = &Null{} } func (it *LinksTo) Clone() graph.Iterator { out := NewLinksTo(it.ts, it.primaryIt.Clone(), it.dir) out.CopyTagsFrom(it) return out } // Return the direction under consideration. func (it *LinksTo) Direction() quad.Direction { return it.dir } // Tag these results, and our subiterator's results. func (it *LinksTo) TagResults(dst map[string]graph.Value) { it.Base.TagResults(dst) it.primaryIt.TagResults(dst) } // DEPRECATED func (it *LinksTo) ResultTree() *graph.ResultTree { tree := graph.NewResultTree(it.Result()) tree.AddSubtree(it.primaryIt.ResultTree()) return tree } // Print the iterator. func (it *LinksTo) DebugString(indent int) string { return fmt.Sprintf("%s(%s %d direction:%s\n%s)", strings.Repeat(" ", indent), it.Type(), it.UID(), it.dir, it.primaryIt.DebugString(indent+4)) } // If it checks in the right direction for the subiterator, it is a valid link // for the LinksTo. func (it *LinksTo) Check(val graph.Value) bool { graph.CheckLogIn(it, val) node := it.ts.TripleDirection(val, it.dir) if it.primaryIt.Check(node) { it.Last = val return graph.CheckLogOut(it, val, true) } return graph.CheckLogOut(it, val, false) } // Return a list containing only our subiterator. func (it *LinksTo) SubIterators() []graph.Iterator { return []graph.Iterator{it.primaryIt} } // Optimize the LinksTo, by replacing it if it can be. func (it *LinksTo) Optimize() (graph.Iterator, bool) { newPrimary, changed := it.primaryIt.Optimize() if changed { it.primaryIt = newPrimary if it.primaryIt.Type() == graph.Null { it.nextIt.Close() return it.primaryIt, true } } // Ask the graph.TripleStore if we can be replaced. Often times, this is a great // optimization opportunity (there's a fixed iterator underneath us, for // example). newReplacement, hasOne := it.ts.OptimizeIterator(it) if hasOne { it.Close() return newReplacement, true } return it, false } // Next()ing a LinksTo operates as described above. func (it *LinksTo) Next() (graph.Value, bool) { graph.NextLogIn(it) val, ok := it.nextIt.Next() if !ok { // Subiterator is empty, get another one candidate, ok := it.primaryIt.Next() if !ok { // We're out of nodes in our subiterator, so we're done as well. return graph.NextLogOut(it, 0, false) } it.nextIt.Close() it.nextIt = it.ts.TripleIterator(it.dir, candidate) // Recurse -- return the first in the next set. return it.Next() } it.Last = val return graph.NextLogOut(it, val, ok) } // Close our subiterators. func (it *LinksTo) Close() { it.nextIt.Close() it.primaryIt.Close() } // We won't ever have a new result, but our subiterators might. func (it *LinksTo) NextResult() bool { return it.primaryIt.NextResult() } // Register the LinksTo. func (it *LinksTo) Type() graph.Type { return graph.LinksTo } // Return a guess as to how big or costly it is to next the iterator. func (it *LinksTo) Stats() graph.IteratorStats { subitStats := it.primaryIt.Stats() // TODO(barakmich): These should really come from the triplestore itself fanoutFactor := int64(20) checkConstant := int64(1) nextConstant := int64(2) return graph.IteratorStats{ NextCost: nextConstant + subitStats.NextCost, CheckCost: checkConstant + subitStats.CheckCost, Size: fanoutFactor * subitStats.Size, } }