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  }