// Cardinality-aware planning helpers (port of the Rust plan/cost.rs). // Every count is an exact resident statistic, never an estimate; a probe // ABSTAINS on Param/NamedParam/Null literals (no value at plan time -- // resolving one would bake a call's value into a shared cached plan), so // autoparam'd templates plan value-independently.
package plan import ( ) // anchorCard is the measured leaf cardinality of anchoring on n (lower = // fewer rows to start from). Mirrors the rank tiers so cardinality never // inverts a rank decision -- it only refines a same-tier tie: bound -> 0, // id seek -> 1, concrete property seek -> exact posting length, labelled // -> label cardinality, else node count. func anchorCard( *ast.NodePat, ast.Expr, map[string]int, map[int]bool, graph.Graph) uint64 { if .Var != "" { if , := [.Var]; && [] { return 0 } if idSeekLiteral(, .Var) != nil || idSeekVar(, .Var, , ) != NoSlot { return 1 } } if len(.Labels) > 0 { := .Labels[0] := int64(-1) // The costed seek is the SAME one scanSource builds (bestPropSeek is the // shared oracle over inline props and WHERE equalities), so the rank that // justifies this anchor can never be a posting list the lowerer discards. // A param seek abstains -- no plan-time value -- and falls through to the // label-wide cardinality below. if , := bestPropSeek(, , ); && !.abstain { = int64(.card) } if .Var != "" { if := textMatchSeek(, .Var); != nil && .needle.Kind == ast.LitStr { if , := .SubstringCandidates(, .field, .needle.S); { := int64(setLen()) if < 0 || < { = } } } } if >= 0 { return uint64() } return .LabelCardinality() } return uint64(.NodeCount()) } // resolveAnchorNodes pins a node pattern to the concrete node id(s) it // anchors on at plan time; ok=false when it can't be pinned (bound, a // param seek with no plan-time value, or label-only). func resolveAnchorNodes( *ast.NodePat, ast.Expr, map[string]int, map[int]bool, graph.Graph) ([]graph.NodeID, bool) { if .Var != "" { if , := [.Var]; && [] { return nil, false } if := idSeekLiteral(, .Var); != nil && .Kind == ast.LitInt { return []graph.NodeID{graph.NodeID(.I)}, true } } if len(.Labels) == 0 { return nil, false } // Resolve the SAME prop scanSource seeks (bestPropSeek), so the exact-degree // read here matches the plan actually built. A param seek abstains (no // plan-time value). if , := bestPropSeek(, , ); && !.abstain { if .inVals != nil { var []graph.NodeID for , := range .inVals { = append(, seekNodes(, .Labels[0], .key, semantics.LitValue())...) } slices.Sort() return slices.Compact(), true } return seekNodes(, .Labels[0], .key, semantics.LitValue(.val)), true } return nil, false } // resolvedFirstHopDegree is the exact fan-out of the FIRST hop when a // pattern is anchored on its resolved start: the real degree of the // resolved node(s) over the first hop's types and direction. One hop only, // an exact resident count, never a multi-hop product. ok=false when the // start can't be resolved or there is no hop. func resolvedFirstHopDegree( *ast.Pattern, ast.Expr, map[string]int, map[int]bool, graph.Graph) (uint64, bool) { if len(.Hops) == 0 { return 0, false } , := resolveAnchorNodes(&.Start, , , , ) if ! { return 0, false } := &.Hops[0].Rel := mapDir(.Dir) var uint64 for , := range { += countNeighbors(, , , .Types) } return , true } // bothEndsUnboundParamSeek reports whether both of a hop pattern's endpoints // are labelled, unbound, and seeked only by a parameter-valued property -- // the shape whose real anchor degree is unknown at plan time (the param has // no value yet) but known at execution. A concrete property on an endpoint // would let the degree probe resolve it, so it is NOT this case; a label-only // endpoint is a genuine unknown with no value arriving later, so also not. func bothEndsUnboundParamSeek( *ast.Pattern, map[string]int, map[int]bool) bool { return isUnboundParamSeek(&.Start, , ) && isUnboundParamSeek(.EndNode(), , ) } // isUnboundParamSeek reports whether n is a labelled, not-yet-bound node whose // only concrete seek is a parameter-valued property (no literal property that // would resolve at plan time). func isUnboundParamSeek( *ast.NodePat, map[string]int, map[int]bool) bool { if .Var != "" { if , := [.Var]; && [] { return false } } if len(.Labels) == 0 { return false } := false for := range .Props { switch .Props[].Val.Kind { case ast.LitParam, ast.LitNamedParam: = true case ast.LitNull: // structural, ignore default: // A concrete literal property resolves at plan time -> not this case. return false } } return } // countNeighbors counts a node's neighbors over dir restricted to types. func countNeighbors( graph.Graph, graph.NodeID, graph.Direction, []string) uint64 { var uint64 if len() == 0 { for range .Neighbors(, ) { ++ } return } for range .NeighborsByType(, , ) { ++ } return }