Skip to content

feat: add graphile-ltree plugin — auto-detect ltree columns, folder fields, containment/glob filters#1056

Merged
pyramation merged 2 commits intomainfrom
feat/graphile-ltree-plugin
May 5, 2026
Merged

feat: add graphile-ltree plugin — auto-detect ltree columns, folder fields, containment/glob filters#1056
pyramation merged 2 commits intomainfrom
feat/graphile-ltree-plugin

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

@pyramation pyramation commented May 5, 2026

Summary

Adds graphile-ltree — a PostGraphile v5 plugin for ltree columns with two presets:

GraphileLtreePreset (base) — raw ltree operators on the native LTree scalar:

  • isAncestorOf (containment <@), isDescendantOf (@>), matchesGlob (lquery ~)

GraphileFolderPreset (extends base) — folder-oriented layer:

  • Renames ltree columns via @name tag: pathpathTree (dot-delimited raw ltree)
  • Adds virtual folder field with clean name: path (slash-delimited, e.g. /projects/alpha/docs)
  • Registers folder operators: within, ancestorOf, glob (accept slash paths)
  • Glob semantics: * = single level, ** = recursive

Both presets auto-detect ltree columns by scanning pgRegistry — no smart tags or config needed. Uses ltree_helpers.to_query() when available, inline SQL fallback otherwise.

ConstructivePreset now uses GraphileFolderPreset for the full folder experience.

# Display
{ allFiles { nodes { path pathTree filename } } }
# path = \"/projects/alpha/docs\", pathTree = \"projects.alpha.docs\"

# Folder operators
allFiles(where: { pathTree: { within: \"/projects/alpha\" } })
allFiles(where: { pathTree: { glob: \"/projects/*/docs\" } })

New package at graphile/graphile-ltree/ with 5 plugins, 13 integration tests, added to CI matrix.

Review & Testing Checklist for Human

  • Verify path returns slash-delimited values and pathTree returns dot-delimited raw ltree
  • Verify folder operators (within, glob, ancestorOf) correctly accept slash-delimited paths
  • Verify * wildcard matches single level only (not recursive) — this was a bug that's now fixed
  • Check that auto-detection works on any table with ltree columns without manual configuration

Notes

Depends on pgpm-ltree-helpers module (PR #1055, merged) for server-side path conversion. The glob→lquery conversion fix (**{1}) is also applied in the upstream helpers (constructive-db PR #1007, pgpm-modules PR #65).

Link to Devin session: https://app.devin.ai/sessions/ffa3ed8652fc412f976accbdc229c88d
Requested by: @pyramation

…ields, containment/glob filters

- LtreeCodecPlugin: ensures ltree codec is properly registered with PostGraphile's native LTree scalar
- LtreeExtensionDetectionPlugin: detects ltree extension by scanning pgRegistry codecs
- LtreeFolderFieldPlugin: adds virtual {column}Folder fields with slash-delimited paths
- Connection filter operators: isAncestorOf (<@), isDescendantOf (@>), matchesGlob (~)
- Glob semantics: * = single level (*{1} in lquery), ** = recursive descent
- Bundled into GraphileLtreePreset and wired into ConstructivePreset
- Added to CI test matrix in run-tests.yaml
- 9 integration tests covering scalar type, folder fields, and all filter operators
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

…erators

GraphileFolderPreset extends GraphileLtreePreset with:
- Renames ltree columns via @name tag: path → pathTree (dot-delimited)
- Adds virtual folder field with clean name: path (slash-delimited)
- Registers folder operators on LTree: within, ancestorOf, glob
- All folder operators accept slash-delimited paths

Two-preset architecture:
- GraphileLtreePreset: base ltree operators (isAncestorOf, isDescendantOf, matchesGlob)
- GraphileFolderPreset: folder layer (includes base + folder fields + folder operators)

ConstructivePreset now uses GraphileFolderPreset for the full folder experience.
@pyramation pyramation merged commit 15e9709 into main May 5, 2026
53 checks passed
@pyramation pyramation deleted the feat/graphile-ltree-plugin branch May 5, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant