github.com/vescale/zgraph@v0.0.0-20230410094002-959c02d50f95/executor/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 executor 16 17 import ( 18 "context" 19 "math" 20 21 "github.com/pingcap/errors" 22 "github.com/vescale/zgraph/catalog" 23 "github.com/vescale/zgraph/codec" 24 "github.com/vescale/zgraph/datum" 25 "github.com/vescale/zgraph/parser/ast" 26 "github.com/vescale/zgraph/parser/model" 27 "github.com/vescale/zgraph/planner" 28 "github.com/vescale/zgraph/storage/kv" 29 "golang.org/x/exp/slices" 30 ) 31 32 type MatchExec struct { 33 baseExecutor 34 35 subgraph *planner.Subgraph 36 37 prepared bool 38 matched map[string]datum.Datum 39 results []datum.Row 40 txn kv.Transaction 41 } 42 43 func (m *MatchExec) Next(ctx context.Context) (datum.Row, error) { 44 if !m.prepared { 45 if err := m.prepare(ctx); err != nil { 46 return nil, err 47 } 48 m.prepared = true 49 } 50 if len(m.results) == 0 { 51 return nil, nil 52 } 53 row := m.results[0] 54 m.results = m.results[1:] 55 return row, nil 56 } 57 58 func (m *MatchExec) prepare(ctx context.Context) error { 59 m.matched = make(map[string]datum.Datum) 60 61 txn, err := m.sc.Store().Begin() 62 if err != nil { 63 return err 64 } 65 m.txn = txn 66 67 return m.search(ctx) 68 } 69 70 // search performs a depth-first search on the graph. 71 func (m *MatchExec) search(ctx context.Context) error { 72 // Enumerate all possible connections to vertices that have not been visited. 73 for _, conn := range m.subgraph.Connections { 74 edge, ok := conn.(*planner.Edge) 75 if !ok { 76 return errors.Errorf("variable-length path is not supported yet") 77 } 78 if _, ok := m.matched[edge.Name().L]; ok { 79 continue 80 } 81 _, srcVisited := m.matched[edge.SrcVarName().L] 82 _, dstVisited := m.matched[edge.DstVarName().L] 83 if !srcVisited && !dstVisited { 84 continue 85 } 86 87 if srcVisited && dstVisited { 88 srcVertexID := m.matched[edge.SrcVarName().L].(*datum.Vertex).ID 89 dstVertexID := m.matched[edge.DstVarName().L].(*datum.Vertex).ID 90 edgeVar, err := m.matchEdge(ctx, edge, srcVertexID, dstVertexID) 91 if err != nil { 92 return err 93 } 94 if edgeVar == nil { 95 return nil 96 } 97 return m.stepEdge(ctx, edge, edgeVar) 98 } 99 100 if !dstVisited { 101 srcID := m.matched[edge.SrcVarName().L].(*datum.Vertex).ID 102 dstVertex := m.subgraph.Vertices[edge.DstVarName().L] 103 return m.iterEdge( 104 ctx, 105 edge, 106 srcID, 107 dstVertex, 108 ast.EdgeDirectionOutgoing, 109 func(edgeVar *datum.Edge, endVar *datum.Vertex) error { 110 m.matched[edge.DstVarName().L] = endVar 111 defer func() { 112 delete(m.matched, edge.DstVarName().L) 113 }() 114 return m.stepEdge(ctx, edge, edgeVar) 115 }, 116 ) 117 } 118 119 if !srcVisited { 120 dstID := m.matched[edge.DstVarName().L].(*datum.Vertex).ID 121 srcVertex := m.subgraph.Vertices[edge.SrcVarName().L] 122 return m.iterEdge( 123 ctx, 124 edge, 125 dstID, 126 srcVertex, 127 ast.EdgeDirectionIncoming, 128 func(edgeVar *datum.Edge, endVar *datum.Vertex) error { 129 m.matched[edge.SrcVarName().L] = endVar 130 defer func() { 131 delete(m.matched, edge.SrcVarName().L) 132 }() 133 return m.stepEdge(ctx, edge, edgeVar) 134 }, 135 ) 136 } 137 } 138 139 // The subgraph is disconnected, so we need to find a new start vertex. 140 for _, vertex := range m.subgraph.Vertices { 141 _, visited := m.matched[vertex.Name.L] 142 if visited { 143 continue 144 } 145 return m.iterVertex(vertex, func(vertexVar *datum.Vertex) error { 146 return m.stepVertex(ctx, vertex, vertexVar) 147 }) 148 } 149 150 return nil 151 } 152 153 func (m *MatchExec) stepVertex(ctx context.Context, vertex *planner.Vertex, vertexVar *datum.Vertex) error { 154 m.matched[vertex.Name.L] = vertexVar 155 defer func() { 156 delete(m.matched, vertex.Name.L) 157 }() 158 if m.isMatched() { 159 m.appendResult() 160 return nil 161 } 162 return m.search(ctx) 163 } 164 165 func (m *MatchExec) stepEdge(ctx context.Context, edge *planner.Edge, edgeVar *datum.Edge) error { 166 m.matched[edge.Name().L] = edgeVar 167 defer func() { 168 delete(m.matched, edge.Name().L) 169 }() 170 if m.isMatched() { 171 m.appendResult() 172 return nil 173 } 174 return m.search(ctx) 175 } 176 177 func (m *MatchExec) isMatched() bool { 178 return len(m.matched) == len(m.subgraph.Vertices)+len(m.subgraph.Connections) 179 } 180 181 func (m *MatchExec) appendResult() { 182 result := make(datum.Row, 0, len(m.subgraph.SingletonVars)) 183 for _, singletonVar := range m.subgraph.SingletonVars { 184 d := m.matched[singletonVar.Name.L] 185 result = append(result, d) 186 } 187 m.results = append(m.results, result) 188 } 189 190 func (m *MatchExec) iterVertex(vertex *planner.Vertex, f func(vertexVar *datum.Vertex) error) error { 191 graph := m.sc.CurrentGraph() 192 lower := codec.VertexKey(graph.Meta().ID, 0) 193 upper := codec.VertexKey(graph.Meta().ID, math.MaxInt64) 194 iter, err := m.txn.Iter(lower, upper) 195 if err != nil { 196 197 return err 198 } 199 defer iter.Close() 200 201 for ; err == nil && iter.Valid(); err = iter.Next() { 202 // TODO: better way to skip edge keys 203 if len(iter.Key()) != codec.VertexKeyLen { 204 continue 205 } 206 _, vertexID, err := codec.ParseVertexKey(iter.Key()) 207 if err != nil { 208 return err 209 } 210 211 vertexVar := &datum.Vertex{ 212 ID: vertexID, 213 } 214 if err := m.decodeVertexValue(iter.Value(), vertexVar); err != nil { 215 return err 216 } 217 if !matchLabels(vertexVar.Labels, vertex.Labels) { 218 continue 219 } 220 221 // Check if the vertex matches the label requirements. 222 if len(vertex.Labels) > 0 { 223 if !slices.ContainsFunc(vertexVar.Labels, func(labelName string) bool { 224 return slices.ContainsFunc(vertex.Labels, func(label *catalog.Label) bool { 225 return label.Meta().Name.L == labelName 226 }) 227 }) { 228 continue 229 } 230 } 231 if err := f(vertexVar); err != nil { 232 return err 233 } 234 } 235 return nil 236 } 237 238 func (m *MatchExec) iterEdge( 239 ctx context.Context, 240 edge *planner.Edge, 241 startID int64, 242 endVertex *planner.Vertex, 243 direction ast.EdgeDirection, 244 f func(edgeVar *datum.Edge, endVar *datum.Vertex) error, 245 ) error { 246 graph := m.sc.CurrentGraph() 247 var lower, upper []byte 248 if direction == ast.EdgeDirectionOutgoing { 249 lower = codec.OutgoingEdgeKey(graph.Meta().ID, startID, 0) 250 upper = codec.OutgoingEdgeKey(graph.Meta().ID, startID, math.MaxInt64) 251 } else { 252 lower = codec.IncomingEdgeKey(graph.Meta().ID, 0, startID) 253 upper = codec.IncomingEdgeKey(graph.Meta().ID, math.MaxInt64, startID) 254 } 255 iter, err := m.txn.Iter(lower, upper) 256 if err != nil { 257 return err 258 } 259 defer iter.Close() 260 261 for ; err == nil && iter.Valid(); err = iter.Next() { 262 var endVertexID int64 263 if direction == ast.EdgeDirectionOutgoing { 264 _, _, endVertexID, err = codec.ParseOutgoingEdgeKey(iter.Key()) 265 } else { 266 _, endVertexID, _, err = codec.ParseIncomingEdgeKey(iter.Key()) 267 } 268 269 edgeVar := &datum.Edge{} 270 if err := m.decodeEdgeValue(iter.Value(), edgeVar); err != nil { 271 return err 272 } 273 if err != nil { 274 return err 275 } 276 if !matchLabels(edgeVar.Labels, edge.Labels) { 277 continue 278 } 279 280 endVar, err := m.matchVertex(ctx, endVertex, endVertexID) 281 if err != nil { 282 return err 283 } 284 if endVar == nil { 285 continue 286 } 287 288 if err := f(edgeVar, endVar); err != nil { 289 return err 290 } 291 } 292 return nil 293 } 294 295 func (m *MatchExec) matchVertex(ctx context.Context, vertex *planner.Vertex, vertexID int64) (*datum.Vertex, error) { 296 graph := m.sc.CurrentGraph() 297 key := codec.VertexKey(graph.Meta().ID, vertexID) 298 val, err := m.txn.Get(ctx, key) 299 if err != nil { 300 if errors.ErrorEqual(err, kv.ErrNotExist) { 301 return nil, nil 302 } 303 return nil, err 304 } 305 vertexVar := &datum.Vertex{ 306 ID: vertexID, 307 } 308 if err := m.decodeVertexValue(val, vertexVar); err != nil { 309 return nil, err 310 } 311 if !matchLabels(vertexVar.Labels, vertex.Labels) { 312 return nil, nil 313 } 314 return vertexVar, nil 315 } 316 317 func (m *MatchExec) matchEdge(ctx context.Context, edge *planner.Edge, srcVertexID, dstVertexID int64) (*datum.Edge, error) { 318 graph := m.sc.CurrentGraph() 319 edgeKey := codec.OutgoingEdgeKey(graph.Meta().ID, srcVertexID, dstVertexID) 320 val, err := m.txn.Get(ctx, edgeKey) 321 if err != nil { 322 if errors.ErrorEqual(err, kv.ErrNotExist) { 323 return nil, nil 324 } 325 return nil, err 326 } 327 edgeVar := &datum.Edge{ 328 SrcID: srcVertexID, 329 DstID: dstVertexID, 330 } 331 if err := m.decodeEdgeValue(val, edgeVar); err != nil { 332 return nil, err 333 } 334 if !matchLabels(edgeVar.Labels, edge.Labels) { 335 return nil, nil 336 } 337 return edgeVar, nil 338 } 339 340 func matchLabels(labelNames []string, labels []*catalog.Label) bool { 341 if len(labels) == 0 { 342 return true 343 } 344 return slices.ContainsFunc(labelNames, func(labelName string) bool { 345 return slices.ContainsFunc(labels, func(label *catalog.Label) bool { 346 return label.Meta().Name.L == labelName 347 }) 348 }) 349 } 350 351 func (m *MatchExec) decodeVertexValue(val []byte, v *datum.Vertex) error { 352 labels, properties, err := m.decodeLabelsAndProperties(val) 353 if err != nil { 354 return err 355 } 356 v.Labels = labels 357 v.Props = properties 358 return nil 359 } 360 361 func (m *MatchExec) decodeEdgeValue(val []byte, v *datum.Edge) error { 362 labels, properties, err := m.decodeLabelsAndProperties(val) 363 if err != nil { 364 return err 365 } 366 v.Labels = labels 367 v.Props = properties 368 return nil 369 } 370 371 func (m *MatchExec) decodeLabelsAndProperties(val []byte) (labels []string, properties map[string]datum.Datum, _ error) { 372 graph := m.sc.CurrentGraph() 373 374 var labelInfos []*model.LabelInfo 375 for _, label := range graph.Labels() { 376 labelInfos = append(labelInfos, label.Meta()) 377 } 378 dec := codec.NewPropertyDecoder(labelInfos, graph.Properties()) 379 380 labelIDs, propertyValues, err := dec.Decode(val) 381 if err != nil { 382 return nil, nil, err 383 } 384 properties = make(map[string]datum.Datum) 385 for labelID := range labelIDs { 386 labels = append(labels, graph.LabelByID(int64(labelID)).Meta().Name.L) 387 } 388 for propID, propVal := range propertyValues { 389 propName := graph.PropertyByID(propID).Name.L 390 properties[propName] = propVal 391 } 392 return labels, properties, nil 393 } 394 395 func (m *MatchExec) Close() error { 396 if m.txn != nil { 397 return m.txn.Rollback() 398 } 399 return nil 400 }