github.com/vescale/zgraph@v0.0.0-20230410094002-959c02d50f95/planner/match.go (about) 1 // Copyright 2022 zGraph 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 planner 16 17 import ( 18 "fmt" 19 "math" 20 "sort" 21 22 "github.com/vescale/zgraph/catalog" 23 "github.com/vescale/zgraph/internal/slicesext" 24 "github.com/vescale/zgraph/parser/ast" 25 "github.com/vescale/zgraph/parser/model" 26 ) 27 28 type LogicalMatch struct { 29 baseLogicalPlan 30 31 Subgraph *Subgraph 32 } 33 34 type PhysicalMatch struct { 35 basePhysicalPlan 36 Subgraph *Subgraph 37 } 38 39 type Vertex struct { 40 Name model.CIStr 41 Labels []*catalog.Label 42 } 43 44 // VertexPairConnection represents a connection between two vertices. 45 type VertexPairConnection interface { 46 Name() model.CIStr 47 AnyDirected() bool 48 SetAnyDirected(anyDirected bool) 49 SrcVarName() model.CIStr 50 SetSrcVarName(name model.CIStr) 51 DstVarName() model.CIStr 52 SetDstVarName(name model.CIStr) 53 } 54 55 var ( 56 _ VertexPairConnection = &Edge{} 57 _ VertexPairConnection = &CommonPathExpression{} 58 _ VertexPairConnection = &VariableLengthPath{} 59 ) 60 61 type baseVertexPairConnection struct { 62 name model.CIStr 63 srcVarName model.CIStr 64 dstVarName model.CIStr 65 anyDirected bool 66 } 67 68 func (b *baseVertexPairConnection) Name() model.CIStr { 69 return b.name 70 } 71 72 func (b *baseVertexPairConnection) SetName(name model.CIStr) { 73 b.name = name 74 } 75 76 func (b *baseVertexPairConnection) SrcVarName() model.CIStr { 77 return b.srcVarName 78 } 79 80 func (b *baseVertexPairConnection) SetSrcVarName(name model.CIStr) { 81 b.srcVarName = name 82 } 83 84 func (b *baseVertexPairConnection) DstVarName() model.CIStr { 85 return b.dstVarName 86 } 87 88 func (b *baseVertexPairConnection) SetDstVarName(name model.CIStr) { 89 b.dstVarName = name 90 } 91 92 func (b *baseVertexPairConnection) AnyDirected() bool { 93 return b.anyDirected 94 } 95 96 func (b *baseVertexPairConnection) SetAnyDirected(anyDirected bool) { 97 b.anyDirected = anyDirected 98 } 99 100 type Edge struct { 101 baseVertexPairConnection 102 103 Labels []*catalog.Label 104 } 105 106 type CommonPathExpression struct { 107 baseVertexPairConnection 108 109 Leftmost *Vertex 110 Rightmost *Vertex 111 Vertices map[string]*Vertex 112 Connections map[string]VertexPairConnection 113 Constraints ast.ExprNode 114 } 115 116 type PathFindingGoal int 117 118 const ( 119 PathFindingAll PathFindingGoal = iota 120 PathFindingReaches 121 PathFindingShortest 122 PathFindingCheapest 123 ) 124 125 type VariableLengthPath struct { 126 baseVertexPairConnection 127 128 Conn VertexPairConnection 129 Goal PathFindingGoal 130 MinHops int64 131 MaxHops int64 132 TopK int64 133 WithTies bool 134 Constraints ast.ExprNode 135 Cost ast.ExprNode 136 HopSrc *Vertex 137 HopDst *Vertex 138 } 139 140 type Subgraph struct { 141 Vertices map[string]*Vertex 142 Connections map[string]VertexPairConnection 143 SingletonVars []*GraphVar 144 GroupVars []*GraphVar 145 } 146 147 // GraphVar represents a graph variable in a MATCH clause. 148 type GraphVar struct { 149 Name model.CIStr 150 Anonymous bool 151 } 152 153 type SubgraphBuilder struct { 154 graph *catalog.Graph 155 macros []*ast.PathPatternMacro 156 paths []*ast.PathPattern 157 158 cpes map[string]*CommonPathExpression 159 vertices map[string]*Vertex 160 connections map[string]VertexPairConnection 161 singletonVars []*GraphVar 162 groupVars []*GraphVar 163 } 164 165 func NewSubgraphBuilder(graph *catalog.Graph) *SubgraphBuilder { 166 return &SubgraphBuilder{ 167 graph: graph, 168 cpes: make(map[string]*CommonPathExpression), 169 vertices: make(map[string]*Vertex), 170 connections: make(map[string]VertexPairConnection), 171 } 172 } 173 174 func (s *SubgraphBuilder) AddPathPatterns(paths ...*ast.PathPattern) *SubgraphBuilder { 175 s.paths = append(s.paths, paths...) 176 return s 177 } 178 179 func (s *SubgraphBuilder) AddPathPatternMacros(macros ...*ast.PathPatternMacro) *SubgraphBuilder { 180 s.macros = append(s.macros, macros...) 181 return s 182 } 183 184 func (s *SubgraphBuilder) Build() (*Subgraph, error) { 185 s.buildVertices() 186 if err := s.buildCommonPathExpressions(); err != nil { 187 return nil, err 188 } 189 if err := s.buildConnections(); err != nil { 190 return nil, err 191 } 192 sort.Slice(s.singletonVars, func(i, j int) bool { 193 return s.singletonVars[i].Name.L < s.singletonVars[j].Name.L 194 }) 195 sort.Slice(s.groupVars, func(i, j int) bool { 196 return s.groupVars[i].Name.L < s.groupVars[j].Name.L 197 }) 198 sg := &Subgraph{ 199 Vertices: s.vertices, 200 Connections: s.connections, 201 SingletonVars: s.singletonVars, 202 GroupVars: s.groupVars, 203 } 204 return sg, nil 205 } 206 207 func (s *SubgraphBuilder) buildCommonPathExpressions() error { 208 for _, m := range s.macros { 209 result, err := s.buildPathPatternMacro(m) 210 if err != nil { 211 return err 212 } 213 s.cpes[m.Name.L] = result 214 } 215 return nil 216 } 217 218 func (s *SubgraphBuilder) buildPathPatternMacro(macro *ast.PathPatternMacro) (*CommonPathExpression, error) { 219 if macro.Path.Tp != ast.PathPatternSimple { 220 return nil, fmt.Errorf("non-simple path in path pattern macro is not supported") 221 } 222 sg, err := NewSubgraphBuilder(s.graph).AddPathPatterns(macro.Path).Build() 223 if err != nil { 224 return nil, err 225 } 226 227 leftmostVarName := macro.Path.Vertices[0].Variable.Name.L 228 rightmostVarName := macro.Path.Vertices[len(macro.Path.Vertices)-1].Variable.Name.L 229 230 cpe := &CommonPathExpression{ 231 Leftmost: sg.Vertices[leftmostVarName], 232 Rightmost: sg.Vertices[rightmostVarName], 233 Vertices: sg.Vertices, 234 Connections: sg.Connections, 235 Constraints: macro.Where, 236 } 237 return cpe, nil 238 } 239 240 func (s *SubgraphBuilder) buildVertices() { 241 astVars := make(map[string]*ast.VariableSpec) 242 for _, path := range s.paths { 243 for _, astVertex := range path.Vertices { 244 astVar := astVertex.Variable 245 if v, ok := s.vertices[astVar.Name.L]; ok { 246 if labels := astVar.Labels; len(labels) > 0 { 247 if len(v.Labels) > 0 { 248 v.Labels = slicesext.FilterFunc(v.Labels, func(l *catalog.Label) bool { 249 return slicesext.ContainsFunc(labels, func(label model.CIStr) bool { 250 return l.Meta().Name.Equal(label) 251 }) 252 }) 253 } else { 254 for _, l := range astVar.Labels { 255 v.Labels = append(v.Labels, s.graph.Label(l.L)) 256 } 257 } 258 } 259 } else { 260 s.vertices[astVar.Name.L] = s.buildVertex(astVar) 261 astVars[astVar.Name.L] = astVar 262 } 263 } 264 } 265 for name := range s.vertices { 266 astVar := astVars[name] 267 s.singletonVars = append(s.singletonVars, &GraphVar{ 268 Name: astVar.Name, 269 Anonymous: astVar.Anonymous, 270 }) 271 } 272 } 273 274 func (s *SubgraphBuilder) buildVertex(astVar *ast.VariableSpec) *Vertex { 275 v := &Vertex{ 276 Name: astVar.Name, 277 } 278 for _, l := range astVar.Labels { 279 v.Labels = append(v.Labels, s.graph.Label(l.L)) 280 } 281 return v 282 } 283 284 func (s *SubgraphBuilder) buildConnections() error { 285 allConns := s.connections 286 for _, path := range s.paths { 287 for i, astConn := range path.Connections { 288 var ( 289 conn VertexPairConnection 290 err error 291 ) 292 switch path.Tp { 293 case ast.PathPatternSimple: 294 conn, err = s.buildSimplePath(astConn) 295 case ast.PathPatternAny, ast.PathPatternAnyShortest, ast.PathPatternAllShortest, ast.PathPatternTopKShortest, 296 ast.PathPatternAnyCheapest, ast.PathPatternAllCheapest, ast.PathPatternTopKCheapest, ast.PathPatternAll: 297 topK := path.TopK 298 conn, err = s.buildVariableLengthPath(path.Tp, topK, astConn) 299 default: 300 return fmt.Errorf("unsupported path pattern type: %d", path.Tp) 301 } 302 303 connName, direction, err := extractConnNameAndDirection(astConn) 304 if err != nil { 305 return err 306 } 307 leftVarName := path.Vertices[i].Variable.Name 308 rightVarName := path.Vertices[i+1].Variable.Name 309 srcVarName, dstVarName, anyDirected, err := resolveAndSrcDstVarName(leftVarName, rightVarName, direction) 310 if err != nil { 311 return err 312 } 313 conn.SetAnyDirected(anyDirected) 314 conn.SetSrcVarName(srcVarName) 315 conn.SetDstVarName(dstVarName) 316 allConns[connName.L] = conn 317 } 318 } 319 return nil 320 } 321 322 func (s *SubgraphBuilder) buildSimplePath(astConn ast.VertexPairConnection) (VertexPairConnection, error) { 323 switch x := astConn.(type) { 324 case *ast.EdgePattern: 325 varName := x.Variable.Name 326 edge := &Edge{} 327 edge.SetName(varName) 328 for _, l := range x.Variable.Labels { 329 edge.Labels = append(edge.Labels, s.graph.Label(l.L)) 330 } 331 s.singletonVars = append(s.singletonVars, &GraphVar{ 332 Name: varName, 333 Anonymous: x.Variable.Anonymous, 334 }) 335 return edge, nil 336 case *ast.ReachabilityPathExpr: 337 conn, err := s.buildConnWithCpe(x.AnonymousName, x.Labels) 338 if err != nil { 339 return nil, err 340 } 341 vlp := &VariableLengthPath{Conn: conn} 342 vlp.SetName(x.AnonymousName) 343 vlp.Goal = PathFindingReaches 344 if x.Quantifier != nil { 345 vlp.MinHops = x.Quantifier.N 346 vlp.MaxHops = x.Quantifier.M & math.MaxInt64 347 } else { 348 vlp.MinHops = 1 349 vlp.MaxHops = 1 350 } 351 s.groupVars = append(s.groupVars, &GraphVar{ 352 Name: x.AnonymousName, 353 Anonymous: true, 354 }) 355 return vlp, nil 356 default: 357 return nil, fmt.Errorf("unsupported ast.VertexPairConnection(%T) in simple path pattern", x) 358 } 359 } 360 361 func (s *SubgraphBuilder) buildVariableLengthPath( 362 pathTp ast.PathPatternType, topK int64, astConn ast.VertexPairConnection, 363 ) (VertexPairConnection, error) { 364 x, ok := astConn.(*ast.QuantifiedPathExpr) 365 if !ok { 366 return nil, fmt.Errorf("unsupported ast.VertexPairConnection(%T) in variable-length path pattern", astConn) 367 } 368 varName := x.Edge.Variable.Name 369 labels := x.Edge.Variable.Labels 370 371 conn, err := s.buildConnWithCpe(varName, labels) 372 if err != nil { 373 return nil, err 374 } 375 vlp := &VariableLengthPath{Conn: conn} 376 377 switch pathTp { 378 case ast.PathPatternAny, ast.PathPatternAnyShortest: 379 vlp.Goal = PathFindingShortest 380 vlp.TopK = 1 381 case ast.PathPatternAllShortest: 382 vlp.Goal = PathFindingShortest 383 vlp.WithTies = true 384 case ast.PathPatternTopKShortest: 385 vlp.Goal = PathFindingShortest 386 vlp.TopK = topK 387 case ast.PathPatternAnyCheapest: 388 vlp.Goal = PathFindingCheapest 389 vlp.TopK = 1 390 case ast.PathPatternAllCheapest: 391 vlp.Goal = PathFindingCheapest 392 vlp.WithTies = true 393 case ast.PathPatternTopKCheapest: 394 vlp.Goal = PathFindingCheapest 395 vlp.TopK = topK 396 case ast.PathPatternAll: 397 vlp.Goal = PathFindingAll 398 } 399 if x.Quantifier != nil { 400 vlp.MinHops = x.Quantifier.N 401 vlp.MaxHops = x.Quantifier.M 402 } else { 403 vlp.MinHops = 1 404 vlp.MaxHops = 1 405 } 406 if x.Quantifier != nil { 407 vlp.MinHops = x.Quantifier.N 408 vlp.MaxHops = x.Quantifier.M 409 } else { 410 vlp.MinHops = 1 411 vlp.MaxHops = 1 412 } 413 vlp.Constraints = x.Where 414 vlp.Cost = x.Cost 415 416 var hopSrcVar, hopDstVar *ast.VariableSpec 417 if x.Source != nil { 418 hopSrcVar = x.Source.Variable 419 } 420 if x.Destination != nil { 421 hopDstVar = x.Destination.Variable 422 } 423 if x.Edge.Direction == ast.EdgeDirectionIncoming { 424 hopSrcVar, hopDstVar = hopDstVar, hopSrcVar 425 } 426 if hopSrcVar != nil { 427 vlp.HopSrc = s.buildVertex(hopSrcVar) 428 s.groupVars = append(s.groupVars, &GraphVar{ 429 Name: hopSrcVar.Name, 430 Anonymous: hopSrcVar.Anonymous, 431 }) 432 } 433 if hopDstVar != nil { 434 vlp.HopDst = s.buildVertex(hopDstVar) 435 s.groupVars = append(s.groupVars, &GraphVar{ 436 Name: hopDstVar.Name, 437 Anonymous: hopDstVar.Anonymous, 438 }) 439 } 440 s.groupVars = append(s.groupVars, &GraphVar{ 441 Name: varName, 442 Anonymous: x.Edge.Variable.Anonymous, 443 }) 444 return vlp, nil 445 } 446 447 // buildConnWithCpe builds a single connection with a CPE. If label and CPE have the same name, the CPE will be used. 448 // Currently, reference multiple CPEs or mix CPEs with normal labels is not supported. 449 func (s *SubgraphBuilder) buildConnWithCpe(varName model.CIStr, labelOrCpeNames []model.CIStr) (VertexPairConnection, error) { 450 edge := &Edge{} 451 edge.SetName(varName) 452 if len(labelOrCpeNames) == 0 { 453 edge.Labels = s.graph.Labels() 454 return edge, nil 455 } 456 457 for _, name := range labelOrCpeNames { 458 if cpe, ok := s.cpes[name.L]; ok { 459 if len(labelOrCpeNames) != 1 { 460 return nil, fmt.Errorf("reference multiple CPEs or mix CPEs with normal labels is not supported") 461 } 462 // TODO: clone the CPE to avoid modifying the original one. 463 return cpe, nil 464 } 465 edge.Labels = append(edge.Labels, s.graph.Label(name.L)) 466 } 467 return edge, nil 468 } 469 470 func resolveAndSrcDstVarName( 471 leftVarName, rightVarName model.CIStr, direction ast.EdgeDirection, 472 ) (srcVarName, dstVarName model.CIStr, anyDirected bool, err error) { 473 switch direction { 474 case ast.EdgeDirectionOutgoing: 475 srcVarName = leftVarName 476 dstVarName = rightVarName 477 case ast.EdgeDirectionIncoming: 478 srcVarName = rightVarName 479 dstVarName = leftVarName 480 case ast.EdgeDirectionAnyDirected: 481 srcVarName = leftVarName 482 dstVarName = rightVarName 483 anyDirected = true 484 default: 485 err = fmt.Errorf("unsupported edge direction: %v", direction) 486 } 487 return 488 } 489 490 func extractConnNameAndDirection(conn ast.VertexPairConnection) (model.CIStr, ast.EdgeDirection, error) { 491 switch x := conn.(type) { 492 case *ast.EdgePattern: 493 return x.Variable.Name, x.Direction, nil 494 case *ast.ReachabilityPathExpr: 495 return x.AnonymousName, x.Direction, nil 496 case *ast.QuantifiedPathExpr: 497 return x.Edge.Variable.Name, x.Edge.Direction, nil 498 default: 499 return model.CIStr{}, 0, fmt.Errorf("unsupported ast.VertexPairConnection(%T)", x) 500 } 501 }