https://github.com/google/cayley
Tip revision: c5c1ada2cebbcd411a606121817274f3fac3abc4 authored by Iddan Aaronsohn on 21 January 2020, 23:53:04 UTC
Use correct IRIs in steps_test
Use correct IRIs in steps_test
Tip revision: c5c1ada
shapes.go
package nosql
import (
"context"
"fmt"
"math"
"strconv"
"github.com/hidal-go/hidalgo/legacy/nosql"
"github.com/cayleygraph/cayley/graph"
"github.com/cayleygraph/cayley/graph/iterator"
"github.com/cayleygraph/cayley/query/shape"
"github.com/cayleygraph/quad"
)
var _ shape.Optimizer = (*QuadStore)(nil)
func (qs *QuadStore) OptimizeShape(ctx context.Context, s shape.Shape) (shape.Shape, bool) {
switch s := s.(type) {
case shape.Quads:
return qs.optimizeQuads(s)
case shape.Filter:
return qs.optimizeFilter(s)
case shape.Page:
return qs.optimizePage(s)
case shape.Composite:
if s2, opt := s.Simplify().Optimize(ctx, qs); opt {
return s2, true
}
}
return s, false
}
// Shape is a shape representing a documents query with filters
type Shape struct {
Collection string // name of the collection
Filters []nosql.FieldFilter // filters to select documents
Limit int64 // limits a number of documents
}
func (s Shape) BuildIterator(qs graph.QuadStore) iterator.Shape {
db, ok := qs.(*QuadStore)
if !ok {
return iterator.NewError(fmt.Errorf("not a nosql database: %T", qs))
}
return db.newIterator(s.Collection, s.Filters...)
}
func (s Shape) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {
return s, false
}
// Quads is a shape representing a quads query
type Quads struct {
Links []Linkage // filters to select quads
Limit int64 // limits a number of documents
}
func (s Quads) BuildIterator(qs graph.QuadStore) iterator.Shape {
db, ok := qs.(*QuadStore)
if !ok {
return iterator.NewError(fmt.Errorf("not a nosql database: %T", qs))
}
return db.newLinksToIterator(colQuads, s.Links)
}
func (s Quads) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {
return s, false
}
const int64Adjust = 1 << 63
// itos serializes int64 into a sortable string 13 chars long.
func itos(i int64) string {
s := strconv.FormatUint(uint64(i)+int64Adjust, 32)
const z = "0000000000000"
return z[len(s):] + s
}
// stoi de-serializes int64 from a sortable string 13 chars long.
func stoi(s string) int64 {
ret, err := strconv.ParseUint(s, 32, 64)
if err != nil {
//TODO handle error?
return 0
}
return int64(ret - int64Adjust)
}
func toFieldFilter(opt *Traits, c shape.Comparison) ([]nosql.FieldFilter, bool) {
var op nosql.FilterOp
switch c.Op {
case iterator.CompareGT:
op = nosql.GT
case iterator.CompareGTE:
op = nosql.GTE
case iterator.CompareLT:
op = nosql.LT
case iterator.CompareLTE:
op = nosql.LTE
default:
return nil, false
}
fieldPath := func(s string) []string {
return []string{fldValue, s}
}
var filters []nosql.FieldFilter
switch v := c.Val.(type) {
case quad.String:
filters = []nosql.FieldFilter{
{Path: fieldPath(fldValData), Filter: op, Value: nosql.String(v)},
{Path: fieldPath(fldIRI), Filter: nosql.NotEqual, Value: nosql.Bool(true)},
{Path: fieldPath(fldBNode), Filter: nosql.NotEqual, Value: nosql.Bool(true)},
}
case quad.IRI:
filters = []nosql.FieldFilter{
{Path: fieldPath(fldValData), Filter: op, Value: nosql.String(v)},
{Path: fieldPath(fldIRI), Filter: nosql.Equal, Value: nosql.Bool(true)},
}
case quad.BNode:
filters = []nosql.FieldFilter{
{Path: fieldPath(fldValData), Filter: op, Value: nosql.String(v)},
{Path: fieldPath(fldBNode), Filter: nosql.Equal, Value: nosql.Bool(true)},
}
case quad.Int:
if opt.Number32 && (v < math.MinInt32 || v > math.MaxInt32) {
// switch to range on string values
filters = []nosql.FieldFilter{
{Path: fieldPath(fldValStrInt), Filter: op, Value: nosql.String(itos(int64(v)))},
}
} else {
filters = []nosql.FieldFilter{
{Path: fieldPath(fldValInt), Filter: op, Value: nosql.Int(v)},
}
}
case quad.Float:
filters = []nosql.FieldFilter{
{Path: fieldPath(fldValFloat), Filter: op, Value: nosql.Float(v)},
}
case quad.Time:
filters = []nosql.FieldFilter{
{Path: fieldPath(fldValTime), Filter: op, Value: nosql.Time(v)},
}
default:
return nil, false
}
return filters, true
}
func (qs *QuadStore) optimizeFilter(s shape.Filter) (shape.Shape, bool) {
if _, ok := s.From.(shape.AllNodes); !ok {
return s, false
}
var (
filters []nosql.FieldFilter
left []shape.ValueFilter
)
fieldPath := func(s string) []string {
return []string{fldValue, s}
}
for _, f := range s.Filters {
switch f := f.(type) {
case shape.Comparison:
if fld, ok := toFieldFilter(&qs.opt, f); ok {
filters = append(filters, fld...)
continue
}
case shape.Wildcard:
filters = append(filters, []nosql.FieldFilter{
{Path: fieldPath(fldValData), Filter: nosql.Regexp, Value: nosql.String(f.Regexp())},
}...)
continue
case shape.Regexp:
filters = append(filters, []nosql.FieldFilter{
{Path: fieldPath(fldValData), Filter: nosql.Regexp, Value: nosql.String(f.Re.String())},
}...)
if !f.Refs {
filters = append(filters, []nosql.FieldFilter{
{Path: fieldPath(fldIRI), Filter: nosql.NotEqual, Value: nosql.Bool(true)},
{Path: fieldPath(fldBNode), Filter: nosql.NotEqual, Value: nosql.Bool(true)},
}...)
}
continue
}
left = append(left, f)
}
if len(filters) == 0 {
return s, false
}
var ns shape.Shape = Shape{Collection: colNodes, Filters: filters}
if len(left) != 0 {
ns = shape.Filter{From: ns, Filters: left}
}
return ns, true
}
func (qs *QuadStore) optimizeQuads(s shape.Quads) (shape.Shape, bool) {
var (
links []Linkage
left []shape.QuadFilter
)
for _, f := range s {
if v, ok := shape.One(f.Values); ok {
if h, ok := v.(NodeHash); ok {
links = append(links, Linkage{Dir: f.Dir, Val: h})
continue
}
}
left = append(left, f)
}
if len(links) == 0 {
return s, false
}
var ns shape.Shape = Quads{Links: links}
if len(left) != 0 {
ns = shape.Intersect{ns, shape.Quads(left)}
}
return s, true
}
func (qs *QuadStore) optimizePage(s shape.Page) (shape.Shape, bool) {
if s.Skip != 0 {
return s, false
}
switch f := s.From.(type) {
case shape.AllNodes:
return Shape{Collection: colNodes, Limit: s.Limit}, false
case Shape:
s.ApplyPage(shape.Page{Limit: f.Limit})
f.Limit = s.Limit
return f, true
case Quads:
s.ApplyPage(shape.Page{Limit: f.Limit})
f.Limit = s.Limit
return f, true
}
return s, false
}