github.com/cayleygraph/cayley@v0.7.7/graph/shape/path.go (about) 1 package shape 2 3 import ( 4 "context" 5 6 "github.com/cayleygraph/cayley/graph" 7 "github.com/cayleygraph/cayley/graph/iterator" 8 "github.com/cayleygraph/quad" 9 ) 10 11 func IntersectShapes(s1, s2 Shape) Shape { 12 switch s1 := s1.(type) { 13 case AllNodes: 14 return s2 15 case Intersect: 16 if s2, ok := s2.(Intersect); ok { 17 return append(s1, s2...) 18 } 19 return append(s1, s2) 20 } 21 return Intersect{s1, s2} 22 } 23 24 func IntersectOptional(s, opt Shape) Shape { 25 var optional []Shape 26 switch opt := opt.(type) { 27 case Intersect: 28 optional = []Shape(opt) 29 case IntersectOpt: 30 optional = make([]Shape, 0, len(opt.Sub)+len(opt.Opt)) 31 optional = append(optional, opt.Sub...) 32 optional = append(optional, opt.Opt...) 33 default: 34 optional = []Shape{opt} 35 } 36 if len(optional) == 0 { 37 return s 38 } 39 switch s := s.(type) { 40 case Intersect: 41 return IntersectOpt{Sub: s, Opt: optional} 42 case IntersectOpt: 43 s.Opt = append(s.Opt, optional...) 44 return s 45 } 46 return IntersectOpt{Sub: Intersect{s}, Opt: optional} 47 } 48 49 func UnionShapes(s1, s2 Shape) Union { 50 if s1, ok := s1.(Union); ok { 51 if s2, ok := s2.(Union); ok { 52 return append(s1, s2...) 53 } 54 return append(s1, s2) 55 } 56 return Union{s1, s2} 57 } 58 59 func buildOut(from, via, labels Shape, tags []string, in bool) Shape { 60 start, goal := quad.Subject, quad.Object 61 if in { 62 start, goal = goal, start 63 } 64 if len(tags) != 0 { 65 via = Save{From: via, Tags: tags} 66 } 67 68 quads := make(Quads, 0, 3) 69 if _, ok := from.(AllNodes); !ok { 70 quads = append(quads, QuadFilter{ 71 Dir: start, Values: from, 72 }) 73 } 74 if _, ok := via.(AllNodes); !ok { 75 quads = append(quads, QuadFilter{ 76 Dir: quad.Predicate, Values: via, 77 }) 78 } 79 if labels != nil { 80 if _, ok := labels.(AllNodes); !ok { 81 quads = append(quads, QuadFilter{ 82 Dir: quad.Label, Values: labels, 83 }) 84 } 85 } 86 return NodesFrom{Quads: quads, Dir: goal} 87 } 88 89 func Out(from, via, labels Shape, tags ...string) Shape { 90 return buildOut(from, via, labels, tags, false) 91 } 92 93 func In(from, via, labels Shape, tags ...string) Shape { 94 return buildOut(from, via, labels, tags, true) 95 } 96 97 // InWithTags, OutWithTags, Both, BothWithTags 98 99 func Predicates(from Shape, in bool) Shape { 100 dir := quad.Subject 101 if in { 102 dir = quad.Object 103 } 104 return Unique{NodesFrom{ 105 Quads: Quads{ 106 {Dir: dir, Values: from}, 107 }, 108 Dir: quad.Predicate, 109 }} 110 } 111 112 func SavePredicates(from Shape, in bool, tag string) Shape { 113 preds := Save{ 114 From: AllNodes{}, 115 Tags: []string{tag}, 116 } 117 start := quad.Subject 118 if in { 119 start = quad.Object 120 } 121 122 var save Shape = NodesFrom{ 123 Quads: Quads{ 124 {Dir: quad.Predicate, Values: preds}, 125 }, 126 Dir: start, 127 } 128 return IntersectShapes(from, save) 129 } 130 131 func Labels(from Shape) Shape { 132 return Unique{NodesFrom{ 133 Quads: Union{ 134 Quads{ 135 {Dir: quad.Subject, Values: from}, 136 }, 137 Quads{ 138 {Dir: quad.Object, Values: from}, 139 }, 140 }, 141 Dir: quad.Label, 142 }} 143 } 144 145 func SaveVia(from, via Shape, tag string, rev, opt bool) Shape { 146 return SaveViaLabels(from, via, AllNodes{}, tag, rev, opt) 147 } 148 149 func SaveViaLabels(from, via, labels Shape, tag string, rev, opt bool) Shape { 150 nodes := Save{ 151 From: AllNodes{}, 152 Tags: []string{tag}, 153 } 154 start, goal := quad.Subject, quad.Object 155 if rev { 156 start, goal = goal, start 157 } 158 159 quads := Quads{ 160 {Dir: goal, Values: nodes}, 161 {Dir: quad.Predicate, Values: via}, 162 } 163 if labels != nil { 164 if _, ok := labels.(AllNodes); !ok { 165 quads = append(quads, QuadFilter{ 166 Dir: quad.Label, Values: labels, 167 }) 168 } 169 } 170 171 var save Shape = NodesFrom{ 172 Quads: quads, 173 Dir: start, 174 } 175 if opt { 176 return IntersectOptional(from, save) 177 } 178 return IntersectShapes(from, save) 179 } 180 181 func Has(from, via, nodes Shape, rev bool) Shape { 182 return HasLabels(from, via, AllNodes{}, nodes, rev) 183 } 184 185 func HasLabels(from, via, nodes, labels Shape, rev bool) Shape { 186 start, goal := quad.Subject, quad.Object 187 if rev { 188 start, goal = goal, start 189 } 190 191 quads := make(Quads, 0, 3) 192 if _, ok := nodes.(AllNodes); !ok { 193 quads = append(quads, QuadFilter{ 194 Dir: goal, Values: nodes, 195 }) 196 } 197 if _, ok := via.(AllNodes); !ok { 198 quads = append(quads, QuadFilter{ 199 Dir: quad.Predicate, Values: via, 200 }) 201 } 202 if labels != nil { 203 if _, ok := labels.(AllNodes); !ok { 204 quads = append(quads, QuadFilter{ 205 Dir: quad.Label, Values: labels, 206 }) 207 } 208 } 209 if len(quads) == 0 { 210 panic("empty has") 211 } 212 return IntersectShapes(from, NodesFrom{ 213 Quads: quads, Dir: start, 214 }) 215 } 216 217 func AddFilters(nodes Shape, filters ...ValueFilter) Shape { 218 if len(filters) == 0 { 219 return nodes 220 } 221 if s, ok := nodes.(Filter); ok { 222 arr := make([]ValueFilter, 0, len(s.Filters)+len(filters)) 223 arr = append(arr, s.Filters...) 224 arr = append(arr, filters...) 225 return Filter{From: s.From, Filters: arr} 226 } 227 if nodes == nil { 228 nodes = AllNodes{} 229 } 230 return Filter{ 231 From: nodes, 232 Filters: filters, 233 } 234 } 235 236 func Compare(nodes Shape, op iterator.Operator, v quad.Value) Shape { 237 return AddFilters(nodes, Comparison{Op: op, Val: v}) 238 } 239 240 func Iterate(ctx context.Context, qs graph.QuadStore, s Shape) *graph.IterateChain { 241 it := BuildIterator(qs, s) 242 return graph.Iterate(ctx, it).On(qs) 243 }