forked from cayleygraph/cayley
-
Notifications
You must be signed in to change notification settings - Fork 0
/
optional_iterator.go
134 lines (118 loc) · 3.94 KB
/
optional_iterator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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 graph
// "Optional" is kind of odd. It's not an iterator in the strictest sense, but
// it's easier to implement as an iterator.
//
// Consider what it means. It means that we have a subconstraint which we do
// not want to constrain the query -- we just want it to return the matching
// subgraph if one matches at all. By analogy to regular expressions, it is the
// '?' operator.
//
// If it were a proper iterator of its own (and indeed, a reasonable refactor
// of this iterator would be to make it such) it would contain an all iterator
// -- all things in the graph. It matches everything (as does the regex "(a)?")
import (
"fmt"
"github.com/barakmich/glog"
"strings"
)
// An optional iterator has the subconstraint iterator we wish to be optional
// and whether the last check we received was true or false.
type OptionalIterator struct {
BaseIterator
subIt Iterator
lastCheck bool
}
// Creates a new optional iterator.
func NewOptionalIterator(it Iterator) *OptionalIterator {
var o OptionalIterator
BaseIteratorInit(&o.BaseIterator)
o.nextable = false
o.subIt = it
return &o
}
func (o *OptionalIterator) Reset() {
o.subIt.Reset()
o.lastCheck = false
}
func (o *OptionalIterator) Close() {
o.subIt.Close()
}
func (o *OptionalIterator) Clone() Iterator {
out := NewOptionalIterator(o.subIt.Clone())
out.CopyTagsFrom(o)
return out
}
// Nexting the iterator is unsupported -- error and return an empty set.
// (As above, a reasonable alternative would be to Next() an all iterator)
func (o *OptionalIterator) Next() (TSVal, bool) {
glog.Errorln("Nexting an un-nextable iterator")
return nil, false
}
// An optional iterator only has a next result if, (a) last time we checked
// we had any results whatsoever, and (b) there was another subresult in our
// optional subbranch.
func (o *OptionalIterator) NextResult() bool {
if o.lastCheck {
return o.subIt.NextResult()
}
return false
}
// Check() is the real hack of this iterator. It always returns true, regardless
// of whether the subiterator matched. But we keep track of whether the subiterator
// matched for results purposes.
func (o *OptionalIterator) Check(val TSVal) bool {
checked := o.subIt.Check(val)
o.lastCheck = checked
o.Last = val
return true
}
// If we failed the check, then the subiterator should not contribute to the result
// set. Otherwise, go ahead and tag it.
func (o *OptionalIterator) TagResults(out *map[string]TSVal) {
if o.lastCheck == false {
return
}
o.subIt.TagResults(out)
}
// Registers the optional iterator.
func (o *OptionalIterator) Type() string { return "optional" }
// Prints the optional and it's subiterator.
func (o *OptionalIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags:%s\n%s)",
strings.Repeat(" ", indent),
o.Type(),
o.Tags(),
o.subIt.DebugString(indent+4))
}
// There's nothing to optimize for an optional. Optimize the subiterator and
// potentially replace it.
func (o *OptionalIterator) Optimize() (Iterator, bool) {
newSub, changed := o.subIt.Optimize()
if changed {
o.subIt.Close()
o.subIt = newSub
}
return o, false
}
// We're only as expensive as our subiterator. Except, we can't be nexted.
func (o *OptionalIterator) GetStats() *IteratorStats {
subStats := o.subIt.GetStats()
return &IteratorStats{
CheckCost: subStats.CheckCost,
NextCost: int64(1 << 62),
Size: subStats.Size,
}
}