github.com/cayleygraph/cayley@v0.7.7/graph/nosql/iterator.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 nosql 16 17 import ( 18 "context" 19 "fmt" 20 21 "github.com/hidal-go/hidalgo/legacy/nosql" 22 23 "github.com/cayleygraph/cayley/clog" 24 "github.com/cayleygraph/cayley/graph" 25 "github.com/cayleygraph/quad" 26 ) 27 28 type Linkage struct { 29 Dir quad.Direction 30 Val NodeHash 31 } 32 33 func linkageToFilters(links []Linkage) []nosql.FieldFilter { 34 filters := make([]nosql.FieldFilter, 0, len(links)) 35 for _, l := range links { 36 filters = append(filters, nosql.FieldFilter{ 37 Path: []string{l.Dir.String()}, 38 Filter: nosql.Equal, 39 Value: nosql.String(l.Val), 40 }) 41 } 42 return filters 43 } 44 45 var _ graph.IteratorFuture = (*Iterator)(nil) 46 47 func NewLinksToIterator(qs *QuadStore, collection string, links []Linkage) *Iterator { 48 it := &Iterator{ 49 it: newLinksToIterator(qs, collection, links), 50 } 51 it.Iterator = graph.NewLegacy(it.it, it) 52 return it 53 } 54 55 func NewIterator(qs *QuadStore, collection string, constraints ...nosql.FieldFilter) *Iterator { 56 it := &Iterator{ 57 it: newIterator(qs, collection, constraints...), 58 } 59 it.Iterator = graph.NewLegacy(it.it, it) 60 return it 61 } 62 63 type Iterator struct { 64 it *iterator2 65 graph.Iterator 66 } 67 68 func (it *Iterator) AsShape() graph.IteratorShape { 69 it.Close() 70 return it.it 71 } 72 73 func (it *Iterator) Sorted() bool { return true } 74 75 var _ graph.IteratorShapeCompat = (*iterator2)(nil) 76 77 type iterator2 struct { 78 qs *QuadStore 79 collection string 80 limit int64 81 constraint []nosql.FieldFilter 82 links []Linkage // used in Contains 83 84 size graph.Size 85 err error 86 } 87 88 func newLinksToIterator(qs *QuadStore, collection string, links []Linkage) *iterator2 { 89 filters := linkageToFilters(links) 90 it := newIterator(qs, collection, filters...) 91 it.links = links 92 return it 93 } 94 95 func newIterator(qs *QuadStore, collection string, constraints ...nosql.FieldFilter) *iterator2 { 96 return &iterator2{ 97 qs: qs, 98 constraint: constraints, 99 collection: collection, 100 size: graph.Size{Size: -1}, 101 } 102 } 103 104 func (it *iterator2) Iterate() graph.Scanner { 105 return newIteratorNext(it.qs, it.collection, it.constraint, it.limit) 106 } 107 108 func (it *iterator2) Lookup() graph.Index { 109 return newIteratorContains(it.qs, it.collection, it.constraint, it.links, it.limit) 110 } 111 112 func (it *iterator2) AsLegacy() graph.Iterator { 113 it2 := &Iterator{it: it} 114 it2.Iterator = graph.NewLegacy(it, it2) 115 return it2 116 } 117 118 func (it *iterator2) SubIterators() []graph.IteratorShape { 119 return nil 120 } 121 122 func (it *iterator2) getSize(ctx context.Context) (graph.Size, error) { 123 if it.size.Size == -1 { 124 size, err := it.qs.getSize(it.collection, it.constraint) 125 if err != nil { 126 it.err = err 127 } 128 it.size = graph.Size{ 129 Size: size, 130 Exact: true, 131 } 132 } 133 if it.limit > 0 && it.size.Size > it.limit { 134 it.size.Size = it.limit 135 } 136 if it.size.Size < 0 { 137 return graph.Size{ 138 Size: it.qs.Size(), 139 Exact: false, 140 }, it.err 141 } 142 return it.size, nil 143 } 144 145 func (it *iterator2) Sorted() bool { return true } 146 func (it *iterator2) Optimize(ctx context.Context) (graph.IteratorShape, bool) { return it, false } 147 148 func (it *iterator2) String() string { 149 return fmt.Sprintf("NoSQL(%v)", it.collection) 150 } 151 152 func (it *iterator2) Stats(ctx context.Context) (graph.IteratorCosts, error) { 153 size, err := it.getSize(ctx) 154 return graph.IteratorCosts{ 155 ContainsCost: 1, 156 NextCost: 5, 157 Size: size, 158 }, err 159 } 160 161 type iteratorNext struct { 162 qs *QuadStore 163 collection string 164 limit int64 165 constraint []nosql.FieldFilter 166 167 iter nosql.DocIterator 168 result graph.Ref 169 err error 170 } 171 172 func newIteratorNext(qs *QuadStore, collection string, constraints []nosql.FieldFilter, limit int64) *iteratorNext { 173 return &iteratorNext{ 174 qs: qs, 175 constraint: constraints, 176 collection: collection, 177 limit: limit, 178 } 179 } 180 181 func (it *iteratorNext) makeIterator() nosql.DocIterator { 182 q := it.qs.db.Query(it.collection) 183 if len(it.constraint) != 0 { 184 q = q.WithFields(it.constraint...) 185 } 186 if it.limit > 0 { 187 q = q.Limit(int(it.limit)) 188 } 189 return q.Iterate() 190 } 191 192 func (it *iteratorNext) Close() error { 193 if it.iter != nil { 194 return it.iter.Close() 195 } 196 return nil 197 } 198 199 func (it *iteratorNext) TagResults(dst map[string]graph.Ref) {} 200 201 func (it *iteratorNext) Next(ctx context.Context) bool { 202 if it.iter == nil { 203 it.iter = it.makeIterator() 204 } 205 var doc nosql.Document 206 for { 207 if !it.iter.Next(ctx) { 208 if err := it.iter.Err(); err != nil { 209 it.err = err 210 clog.Errorf("error nexting iterator: %v", err) 211 } 212 return false 213 } 214 doc = it.iter.Doc() 215 if it.collection == colQuads && !checkQuadValid(doc) { 216 continue 217 } 218 break 219 } 220 if it.collection == colQuads { 221 sh, _ := doc[fldSubject].(nosql.String) 222 ph, _ := doc[fldPredicate].(nosql.String) 223 oh, _ := doc[fldObject].(nosql.String) 224 lh, _ := doc[fldLabel].(nosql.String) 225 it.result = QuadHash{ 226 string(sh), string(ph), string(oh), string(lh), 227 } 228 } else { 229 id, _ := doc[fldHash].(nosql.String) 230 it.result = NodeHash(id) 231 } 232 return true 233 } 234 235 func (it *iteratorNext) Err() error { 236 return it.err 237 } 238 239 func (it *iteratorNext) Result() graph.Ref { 240 return it.result 241 } 242 243 func (it *iteratorNext) NextPath(ctx context.Context) bool { 244 return false 245 } 246 247 func (it *iteratorNext) Sorted() bool { return true } 248 249 func (it *iteratorNext) String() string { 250 return fmt.Sprintf("NoSQLNext(%v)", it.collection) 251 } 252 253 type iteratorContains struct { 254 qs *QuadStore 255 collection string 256 limit int64 // FIXME(dennwc): doesn't work right now 257 constraint []nosql.FieldFilter 258 links []Linkage 259 260 iter nosql.DocIterator 261 result graph.Ref 262 err error 263 } 264 265 func newIteratorContains(qs *QuadStore, collection string, constraints []nosql.FieldFilter, links []Linkage, limit int64) *iteratorContains { 266 return &iteratorContains{ 267 qs: qs, 268 collection: collection, 269 constraint: constraints, 270 links: links, 271 limit: limit, 272 } 273 } 274 275 func (it *iteratorContains) makeIterator() nosql.DocIterator { 276 q := it.qs.db.Query(it.collection) 277 if len(it.constraint) != 0 { 278 q = q.WithFields(it.constraint...) 279 } 280 if it.limit > 0 { 281 q = q.Limit(int(it.limit)) 282 } 283 return q.Iterate() 284 } 285 286 func (it *iteratorContains) Close() error { 287 if it.iter != nil { 288 return it.iter.Close() 289 } 290 return nil 291 } 292 293 func (it *iteratorContains) TagResults(dst map[string]graph.Ref) {} 294 295 func (it *iteratorContains) Err() error { 296 return it.err 297 } 298 299 func (it *iteratorContains) Result() graph.Ref { 300 return it.result 301 } 302 303 func (it *iteratorContains) NextPath(ctx context.Context) bool { 304 return false 305 } 306 307 func (it *iteratorContains) Contains(ctx context.Context, v graph.Ref) bool { 308 if len(it.links) != 0 { 309 qh := v.(QuadHash) 310 for _, l := range it.links { 311 if l.Val != NodeHash(qh.Get(l.Dir)) { 312 return false 313 } 314 } 315 it.result = v 316 return true 317 } 318 if len(it.constraint) == 0 { 319 it.result = v 320 return true 321 } 322 qv := it.qs.NameOf(v) 323 if qv == nil { 324 return false 325 } 326 d := toDocumentValue(&it.qs.opt, qv) 327 for _, f := range it.constraint { 328 if !f.Matches(d) { 329 return false 330 } 331 } 332 it.result = v 333 return true 334 } 335 336 func (it *iteratorContains) Sorted() bool { return true } 337 338 func (it *iteratorContains) String() string { 339 return fmt.Sprintf("NoSQLContains(%v)", it.collection) 340 }