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  }