Skip to content

query_graph: add split / substring — the Cypher subset can filter rows but not shape them #2287

Description

@hamilton-sky

query_graph: add split / substring — the Cypher subset can filter rows but not shape them

Version: 0.10.8 (macOS, single static binary)

What happens

query_graph(query='MATCH (f:Function) WHERE f.qualified_name CONTAINS "safe_null"
                   RETURN split(f.qualified_name, ".") AS parts LIMIT 3', project="…")
→ unsupported function 'split' (supported: count, sum, avg, min, max, collect, toLower,
  toUpper, toString, toInteger, toFloat, toBoolean, size, length, trim, ltrim, rtrim,
  reverse, labels, type, id, keys, properties)

The error is good — it prints the whole supported list, which is how I knew immediately what I had
to work with rather than guessing function by function.

The gap the list shows

That vocabulary covers predicates well and projection barely. Everything string-shaped in it
either tests a value (toLower for case-insensitive comparison), measures one (size, length), or
strips whitespace (trim, ltrim, rtrim). Nothing takes a piece out of a string.

reverse is the tell: a function that manipulates a string is already in the evaluator, so this is
a coverage gap in an existing capability rather than a new class of feature.

Why it matters for this tool specifically

Qualified names are long, and this graph is largely made of them. A query that wants the last
segment of packages/cloudbay-core-cli/frontend/src/pages/action-set-run-view.PermutationDetail,
or the module prefix without the leaf, cannot express it — so the rows come back at full width and
the caller either post-processes them or reads 200-character lines.

That post-processing is exactly the thing an MCP backend is supposed to make unnecessary. The rows
are already crossing the wire at full length and being paid for in tokens, and the trimming that
would have made them readable is a string operation the query could have done at the source.

Concretely, what I wanted was shape-the-output, not filter-the-rows:

MATCH (f:Function)
WHERE f.transitive_loop_depth >= 3
RETURN split(f.qualified_name, ".")[-1] AS name, f.transitive_loop_depth
ORDER BY f.transitive_loop_depth DESC

Suggested fix

In rough order of how much each one buys:

  1. split(string, delimiter) → list — the one that unlocks qualified-name handling.
  2. substring(string, start [, length]) — the general escape hatch; with size already present
    it covers most truncation without a dedicated function.
  3. replace(original, search, replacement) — commonly paired with the two above.
  4. left / right — sugar over substring, only if they are cheap.

split alone would cover most of it. If list indexing on a split result is not supported by the
evaluator, substring plus the existing size is a workable substitute and may be the smaller
change.

Severity

Minor. Nothing here is wrong or misleading — it is a ceiling on what the query language can express,
and the error message makes the ceiling visible immediately rather than letting someone discover it
by trial. Filing it because the fix looks small relative to how often long qualified names come up
in a graph whose primary key is a qualified name.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cypherCypher query language parser/executor bugsparsing/qualityGraph extraction bugs, false positives, missing edgesux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions