github.com/cayleygraph/cayley@v0.7.7/graph/iterator/value_filter.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 import ( 18 "context" 19 20 "github.com/cayleygraph/cayley/graph" 21 "github.com/cayleygraph/quad" 22 ) 23 24 var _ graph.IteratorFuture = &ValueFilter{} 25 26 type ValueFilter struct { 27 it *valueFilter 28 graph.Iterator 29 } 30 31 type ValueFilterFunc func(quad.Value) (bool, error) 32 33 func NewValueFilter(qs graph.Namer, sub graph.Iterator, filter ValueFilterFunc) *ValueFilter { 34 it := &ValueFilter{ 35 it: newValueFilter(qs, graph.AsShape(sub), filter), 36 } 37 it.Iterator = graph.NewLegacy(it.it, it) 38 return it 39 } 40 41 func (it *ValueFilter) AsShape() graph.IteratorShape { 42 it.Close() 43 return it.it 44 } 45 46 var _ graph.IteratorShapeCompat = (*valueFilter)(nil) 47 48 type valueFilter struct { 49 sub graph.IteratorShape 50 filter ValueFilterFunc 51 qs graph.Namer 52 } 53 54 func newValueFilter(qs graph.Namer, sub graph.IteratorShape, filter ValueFilterFunc) *valueFilter { 55 return &valueFilter{ 56 sub: sub, 57 qs: qs, 58 filter: filter, 59 } 60 } 61 62 func (it *valueFilter) Iterate() graph.Scanner { 63 return newValueFilterNext(it.qs, it.sub.Iterate(), it.filter) 64 } 65 66 func (it *valueFilter) Lookup() graph.Index { 67 return newValueFilterContains(it.qs, it.sub.Lookup(), it.filter) 68 } 69 70 func (it *valueFilter) AsLegacy() graph.Iterator { 71 it2 := &ValueFilter{it: it} 72 it2.Iterator = graph.NewLegacy(it, it2) 73 return it2 74 } 75 76 func (it *valueFilter) SubIterators() []graph.IteratorShape { 77 return []graph.IteratorShape{it.sub} 78 } 79 80 func (it *valueFilter) String() string { 81 return "ValueFilter" 82 } 83 84 // There's nothing to optimize, locally, for a value-comparison iterator. 85 // Replace the underlying iterator if need be. 86 // potentially replace it. 87 func (it *valueFilter) Optimize(ctx context.Context) (graph.IteratorShape, bool) { 88 newSub, changed := it.sub.Optimize(ctx) 89 if changed { 90 it.sub = newSub 91 } 92 return it, true 93 } 94 95 // We're only as expensive as our subiterator. 96 // Again, optimized value comparison iterators should do better. 97 func (it *valueFilter) Stats(ctx context.Context) (graph.IteratorCosts, error) { 98 st, err := it.sub.Stats(ctx) 99 st.Size.Size = st.Size.Size/2 + 1 100 st.Size.Exact = false 101 return st, err 102 } 103 104 type valueFilterNext struct { 105 sub graph.Scanner 106 filter ValueFilterFunc 107 qs graph.Namer 108 result graph.Ref 109 err error 110 } 111 112 func newValueFilterNext(qs graph.Namer, sub graph.Scanner, filter ValueFilterFunc) *valueFilterNext { 113 return &valueFilterNext{ 114 sub: sub, 115 qs: qs, 116 filter: filter, 117 } 118 } 119 120 func (it *valueFilterNext) doFilter(val graph.Ref) bool { 121 qval := it.qs.NameOf(val) 122 ok, err := it.filter(qval) 123 if err != nil { 124 it.err = err 125 } 126 return ok 127 } 128 129 func (it *valueFilterNext) Close() error { 130 return it.sub.Close() 131 } 132 133 func (it *valueFilterNext) Next(ctx context.Context) bool { 134 for it.sub.Next(ctx) { 135 val := it.sub.Result() 136 if it.doFilter(val) { 137 it.result = val 138 return true 139 } 140 } 141 it.err = it.sub.Err() 142 return false 143 } 144 145 func (it *valueFilterNext) Err() error { 146 return it.err 147 } 148 149 func (it *valueFilterNext) Result() graph.Ref { 150 return it.result 151 } 152 153 func (it *valueFilterNext) NextPath(ctx context.Context) bool { 154 return it.sub.NextPath(ctx) 155 } 156 157 // If we failed the check, then the subiterator should not contribute to the result 158 // set. Otherwise, go ahead and tag it. 159 func (it *valueFilterNext) TagResults(dst map[string]graph.Ref) { 160 it.sub.TagResults(dst) 161 } 162 163 func (it *valueFilterNext) String() string { 164 return "ValueFilterNext" 165 } 166 167 type valueFilterContains struct { 168 sub graph.Index 169 filter ValueFilterFunc 170 qs graph.Namer 171 result graph.Ref 172 err error 173 } 174 175 func newValueFilterContains(qs graph.Namer, sub graph.Index, filter ValueFilterFunc) *valueFilterContains { 176 return &valueFilterContains{ 177 sub: sub, 178 qs: qs, 179 filter: filter, 180 } 181 } 182 183 func (it *valueFilterContains) doFilter(val graph.Ref) bool { 184 qval := it.qs.NameOf(val) 185 ok, err := it.filter(qval) 186 if err != nil { 187 it.err = err 188 } 189 return ok 190 } 191 192 func (it *valueFilterContains) Close() error { 193 return it.sub.Close() 194 } 195 196 func (it *valueFilterContains) Err() error { 197 return it.err 198 } 199 200 func (it *valueFilterContains) Result() graph.Ref { 201 return it.result 202 } 203 204 func (it *valueFilterContains) NextPath(ctx context.Context) bool { 205 return it.sub.NextPath(ctx) 206 } 207 208 func (it *valueFilterContains) Contains(ctx context.Context, val graph.Ref) bool { 209 if !it.doFilter(val) { 210 return false 211 } 212 ok := it.sub.Contains(ctx, val) 213 if !ok { 214 it.err = it.sub.Err() 215 } 216 return ok 217 } 218 219 // If we failed the check, then the subiterator should not contribute to the result 220 // set. Otherwise, go ahead and tag it. 221 func (it *valueFilterContains) TagResults(dst map[string]graph.Ref) { 222 it.sub.TagResults(dst) 223 } 224 225 func (it *valueFilterContains) String() string { 226 return "ValueFilterContains" 227 }