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 15package nosql 16 17import ( 18 "context" 19 "fmt" 20 21 "github.com/cayleygraph/cayley/clog" 22 "github.com/cayleygraph/cayley/graph" 23 "github.com/cayleygraph/cayley/graph/iterator" 24 "github.com/cayleygraph/cayley/quad" 25) 26 27var _ graph.Iterator = (*Iterator)(nil) 28 29type Linkage struct { 30 Dir quad.Direction 31 Val NodeHash 32} 33 34type Iterator struct { 35 uid uint64 36 tags graph.Tagger 37 qs *QuadStore 38 collection string 39 limit int64 40 constraint []FieldFilter 41 links []Linkage // used in Contains 42 43 iter DocIterator 44 result graph.Value 45 size int64 46 err error 47} 48 49func NewLinksToIterator(qs *QuadStore, collection string, links []Linkage) *Iterator { 50 filters := make([]FieldFilter, 0, len(links)) 51 for _, l := range links { 52 filters = append(filters, FieldFilter{ 53 Path: []string{l.Dir.String()}, 54 Filter: Equal, 55 Value: String(l.Val), 56 }) 57 } 58 it := NewIterator(qs, collection, filters...) 59 it.links = links 60 return it 61} 62 63func (it *Iterator) makeIterator() DocIterator { 64 q := it.qs.db.Query(it.collection) 65 if len(it.constraint) != 0 { 66 q = q.WithFields(it.constraint...) 67 } 68 if it.limit > 0 { 69 q = q.Limit(int(it.limit)) 70 } 71 return q.Iterate() 72} 73 74func NewAllIterator(qs *QuadStore, collection string) *Iterator { 75 return NewIterator(qs, collection) 76} 77 78func NewIterator(qs *QuadStore, collection string, constraints ...FieldFilter) *Iterator { 79 return &Iterator{ 80 uid: iterator.NextUID(), 81 qs: qs, 82 constraint: constraints, 83 collection: collection, 84 size: -1, 85 } 86} 87 88func (it *Iterator) UID() uint64 { 89 return it.uid 90} 91 92func (it *Iterator) Reset() { 93 it.Close() 94 it.iter = it.makeIterator() 95} 96 97func (it *Iterator) Close() error { 98 if it.iter != nil { 99 return it.iter.Close() 100 } 101 return nil 102} 103 104func (it *Iterator) Tagger() *graph.Tagger { 105 return &it.tags 106} 107 108func (it *Iterator) TagResults(dst map[string]graph.Value) { 109 it.tags.TagResult(dst, it.Result()) 110} 111 112func (it *Iterator) Clone() graph.Iterator { 113 var m *Iterator 114 if len(it.links) == 0 { 115 m = NewIterator(it.qs, it.collection, it.constraint...) 116 } else { 117 m = NewLinksToIterator(it.qs, it.collection, it.links) 118 } 119 m.tags.CopyFrom(it) 120 return m 121} 122 123func (it *Iterator) Next(ctx context.Context) bool { 124 if it.iter == nil { 125 it.iter = it.makeIterator() 126 } 127 var doc Document 128 for { 129 if !it.iter.Next(ctx) { 130 if err := it.iter.Err(); err != nil { 131 it.err = err 132 clog.Errorf("error nexting iterator: %v", err) 133 } 134 return false 135 } 136 doc = it.iter.Doc() 137 if it.collection == colQuads && !checkQuadValid(doc) { 138 continue 139 } 140 break 141 } 142 if it.collection == colQuads { 143 sh, _ := doc[fldSubject].(String) 144 ph, _ := doc[fldPredicate].(String) 145 oh, _ := doc[fldObject].(String) 146 lh, _ := doc[fldLabel].(String) 147 it.result = QuadHash{ 148 string(sh), string(ph), string(oh), string(lh), 149 } 150 } else { 151 id, _ := doc[fldHash].(String) 152 it.result = NodeHash(id) 153 } 154 return true 155} 156 157func (it *Iterator) Err() error { 158 return it.err 159} 160 161func (it *Iterator) Result() graph.Value { 162 return it.result 163} 164 165func (it *Iterator) NextPath(ctx context.Context) bool { 166 return false 167} 168 169func (it *Iterator) SubIterators() []graph.Iterator { 170 return nil 171} 172 173func (it *Iterator) Contains(ctx context.Context, v graph.Value) bool { 174 if len(it.links) != 0 { 175 qh := v.(QuadHash) 176 for _, l := range it.links { 177 if l.Val != NodeHash(qh.Get(l.Dir)) { 178 return false 179 } 180 } 181 it.result = v 182 return true 183 } 184 if len(it.constraint) == 0 { 185 it.result = v 186 return true 187 } 188 qv := it.qs.NameOf(v) 189 if qv == nil { 190 return false 191 } 192 d := it.qs.opt.toDocumentValue(qv) 193 for _, f := range it.constraint { 194 if !f.Matches(d) { 195 return false 196 } 197 } 198 it.result = v 199 return true 200} 201 202func (it *Iterator) Size() (int64, bool) { 203 if it.size == -1 { 204 var err error 205 it.size, err = it.qs.getSize(it.collection, it.constraint) 206 if err != nil { 207 it.err = err 208 } 209 } 210 if it.limit > 0 && it.size > it.limit { 211 it.size = it.limit 212 } 213 if it.size < 0 { 214 return it.qs.Size(), false 215 } 216 return it.size, true 217} 218 219func (it *Iterator) Type() graph.Type { 220 if len(it.constraint) == 0 { 221 return graph.All 222 } 223 return "nosql" 224} 225 226func (it *Iterator) Sorted() bool { return true } 227func (it *Iterator) Optimize() (graph.Iterator, bool) { return it, false } 228 229func (it *Iterator) String() string { 230 return fmt.Sprintf("NoSQL(%v)", it.collection) 231} 232 233func (it *Iterator) Stats() graph.IteratorStats { 234 size, exact := it.Size() 235 return graph.IteratorStats{ 236 ContainsCost: 1, 237 NextCost: 5, 238 Size: size, 239 ExactSize: exact, 240 } 241} 242