// Shortest-path stage lowering (port of the Rust plan/sp.rs): turn an// ANY/ALL SHORTEST path-search clause into an SpStage, validating the// optional weight, extracting the per-hop predicate, and resolving the// bound endpoint slots.
package planimport ()func buildSpStage( *stageSpec, map[string]int, map[int]bool, *int) (*SpStage, error) { := .patternif .all && .weight != nil {returnnil, planErrf("ALL SHORTEST does not support weighted path selection") }iflen(.Hops) != 1 { := "ANY SHORTEST"if .all { = "ALL SHORTEST" }returnnil, planErrf("%s requires exactly one relationship (a)-[:T]->{..}(b)", ) } := &.Hops[0].Rel := &.Hops[0].Node , := endpointSlot(&.Start, )if != nil {returnnil, } , := endpointSlot(, )if != nil {returnnil, }var *uint64if .Length != nil { = .Length.Max } , := extractSpHopPred(.where, .Var)if != nil {returnnil, }// A bare `rel.prop` weight narrows to the property-reader form -- the // same per-edge value without per-edge expression evaluation. A fresh // CostSpec keeps the AST (shared with the plan cache) unmutated. := .weightif != nil && .Kind == ast.CostExpr {if , := .Expr.(*ast.Prop); && .Var != "" && .Var == .Var { = &ast.CostSpec{Kind: ast.CostProperty, Prop: .Key} } }// A per-edge weight expression evaluates in the edge scope: it may // reference only the path's relationship variable (bound per hop). := ""if != nil && .Kind == ast.CostExpr {if .Var == "" {returnnil, planErrf("a weighted path-search weight expression requires a named relationship variable") }if := validateWeightExpr(.Expr, []string{.Var}); != nil {returnnil, }if := semantics.CheckRefs(.Expr, map[string]int{.Var: 0}); != nil {returnnil, } = .Var } := * *++ [.pathVar] = [] = truereturn &SpStage{PathSlot: ,From: ,To: ,Dir: mapDir(.Dir),Types: .Types,Max: ,Optional: .optional,All: .all,Weight: ,WeightVar: ,RelPred: , }, nil}// validateWeightExpr checks that a per-edge weight's free variables are// only the allowed ones (the rel variable). A correlated EXISTS/COUNT// subquery is permitted: the shared free-variable walker scopes its// pattern variables as locals for its WHERE.func validateWeightExpr( ast.Expr, []string) error {if := freeVarsOutside(, ); len() > 0 {returnplanErrf("a weighted path-search weight expression may reference only the relationship variable `%s` (per-edge), optionally via correlated EXISTS/COUNT subqueries: references `%s`", [0], [0]) }returnnil}// extractSpHopPred lifts the all(r IN rels(e) WHERE ...) per-hop predicate// from a path-search WHERE; the whole WHERE must reduce to that one form.func extractSpHopPred( ast.Expr, string) (*RelHopPred, error) {if == nil {returnnil, nil }var []ast.ExprSplitAnd(, &)var *RelHopPredfor , := range { , := .(*ast.ListPred)if && != "" && relsArg(.List) == {if .Quant != ast.QuantAll {returnnil, planErrf("only `all(r IN rels(e) WHERE ...)` is supported over a path-search relationship") }if := predRefsOnly(.Pred, .Var); != nil {returnnil, }if != nil {returnnil, planErrf("multiple per-hop predicates on one path search are not supported") } = &RelHopPred{Var: .Var, Pred: .Pred}continue }returnnil, planErrf("a WHERE on a path search is only supported as `all(r IN rels(e) WHERE ...)` over its relationship variable") }return , nil}// endpointSlot resolves a path-search endpoint to its bound slot.func endpointSlot( *ast.NodePat, map[string]int) (int, error) {if .Var == "" {return0, planErrf("path-search endpoints must be named, bound variables (Tier 1)") } , := [.Var]if ! {return0, planErrf("path-search endpoint `%s` must be a bound variable (Tier 1)", .Var) }return , nil}
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.