Skip to content

feat(unigraph): materialize searchable __canonical_name_prefix column#2255

Open
shrugs wants to merge 7 commits into
mainfrom
worktree-canonical-name-prefix
Open

feat(unigraph): materialize searchable __canonical_name_prefix column#2255
shrugs wants to merge 7 commits into
mainfrom
worktree-canonical-name-prefix

Conversation

@shrugs
Copy link
Copy Markdown
Member

@shrugs shrugs commented Jun 4, 2026

Reviewer Focus (Read This First)

three things:

  1. the three write paths in canonicality-db-helpers.ts — does every place that writes canonical_name now also write __canonical_name_prefix consistently (insert, cascade flip, label heal)? the cascadeLabelHeal restructure into a CTE is the only non-mechanical bit.
  2. the explicit DB column name t.text("__canonical_name_prefix") — ponder's default casing strips leading underscores (__hasChildrenhas_children), so the explicit name is load-bearing for the raw-SQL SET __canonical_name_prefix = ... clauses to resolve.
  3. the 64-char cap and that exact eq/in matches still hit the full canonical_name, not the prefix.

Problem & Motivation

  • canonical_name can be arbitrarily long (spam names hit thousands of chars), so it can't go in a plain btree — the per-tuple size limit blows up.
  • today NAME ordering / Domain.subdomains rely on a composite expression index (registry_id, left(canonical_name, 256), id), and every query has to replicate that exact left(canonical_name, 256) expression in ORDER BY/keyset to hit the index. invisible and error-prone for anyone writing SQL directly against ENSDb.
  • we want the full name for correctness + exact match, and a separate, efficiently-indexed, length-capped column for prefix/typeahead search.

What Changed (Concrete)

  1. new materialized column domains.__canonical_name_prefix (Name, explicit DB name to keep the __) — first 64 code points of canonical_name, NULL iff canonical = false.
  2. shared CANONICAL_NAME_PREFIX_LENGTH = 64 + truncateCanonicalNamePrefix() in ensdb-sdk (code-point slice, byte-identical to postgres left(text, N)), consumed by both indexer and api.
  3. indexes repointed onto the prefix column: composite (registry_id, __canonical_name_prefix, id) btree for ordering, GIN trigram for LIKE 'vit%' + substring. canonical_name hash index kept for eq/in.
  4. canonicality-db-helpers.ts maintains the column in all three write paths: ensureDomainInRegistry, cascadeCanonicality (CTE), cascadeLabelHeal (restructured to compute the healed name once in a CTE, then set both columns). no triggers — ponder doesn't support them on managed tables.
  5. omnigraph: name.starts_with and NAME order/cursor now target __canonical_name_prefix; eq/in stay on canonical_name. GraphQL still returns canonical_name everywhere — the prefix column is never exposed.
  6. docs: schema-reference column + index entries, and a prefix-search SQL example.

Design & Planning

  • planned upfront; two decisions confirmed before implementing: 64-char cap (vs 128/256), and keep %like% substring support (so GIN trigram stays, on the prefix column, and the resolver keeps ilike rather than switching to btree + lowercased LIKE).

  • alternative considered: btree text_pattern_ops + case-sensitive LIKE (lowercase input). rejected once we decided to keep substring search — GIN already serves prefix case-insensitively, so ilike preserves exact current behavior with no input-normalization hack.

  • net effect is the same index shapes as before (composite btree for ordering + GIN trigram for fuzzy), just pointed at a real length-capped column instead of left(canonical_name, 256) / the full name.

  • Planning artifacts: internal plan (worktree)

  • Reviewed / approved by: n/a

Self-Review

  • Bugs caught: confirmed ponder strips the leading __ in default casing (__hasChildrenhas_children), which would have silently broken the raw-SQL SET clauses — fixed by passing the explicit column name.
  • Logic simplified: cascadeLabelHeal computes the string_agg name once in a CTE instead of running the correlated subquery twice (once per column).
  • Naming / terminology improved: __ prefix marks it internal, mirroring Registry.__hasChildren; docs explicitly steer canonical_name for exact/display vs __canonical_name_prefix for search.
  • Dead / unnecessary code removed: dropped the now-unused CANONICAL_NAME_SORT_PREFIX constant and its long doc block in the resolver helpers; truncateNameForCursor now delegates to the shared helper.

Cross-Codebase Alignment

  • Search terms used: canonical_name, canonicalName, CANONICAL_NAME_SORT_PREFIX, truncateNameForCursor, byCanonicalNameFuzzy, left(, __hasChildren, has_children, starts_with.
  • Reviewed but unchanged: labels-table GIN trigram on interpreted (separate concern); ENSDB_SCHEMA_CHECKSUM (computed at runtime via getDrizzleSchemaChecksum, so it auto-reflects the new column — no stale value); the GraphQL Domain object (explicit Pothos fields, so the new column isn't auto-exposed).
  • Deferred alignment: none.

Downstream & Consumer Impact

  • Public APIs affected: none. omnigraph GraphQL schema is unchanged (starts_with/eq/in identical), so no pnpm generate. requires a reindex (schema change).
  • Docs updated: unigraph/schema-reference.mdx (column + indexes), unigraph/examples/domain-by-name.mdx (prefix-search snippet).
  • Naming decisions worth calling out: the __ is intentionally part of the SQL column name to signal "internal implementation detail — don't reach for this for exact match/display."

Testing Evidence

  • Testing performed: pnpm typecheck (ensdb-sdk, ensindexer, ensapi), pnpm lint, unit tests --project ensapi --project ensindexer (511 passed) and --project @ensnode/ensdb-sdk (58 passed, incl. schema checksum). full integration CI pnpm test:integration:ci (docker devnet → index → ENSApi → integration suite): 16 files, 350 passed / 2 todo.
  • Known gaps: no dedicated unit test asserting __canonical_name_prefix == left(canonical_name, 64) on the write path; covered indirectly by the integration find-domains/pagination suite.
  • What reviewers reason about manually: that the prefix truncation can't drop a real match for typeahead (input shorter than 64 chars is unaffected; only ordering ties among >64-char-shared-prefix spam names tie-break by id).

Scope Reductions

  • Follow-ups: none required.
  • Why they were deferred: n/a.

Risk Analysis

  • Risk areas: write-path consistency across the three helpers; the raw-SQL column name resolving correctly.
  • Mitigations or rollback options: behavior-preserving by construction (same index shapes, same ilike semantics, same ordering collation); reindex rebuilds the column from scratch. revert is a clean single-commit rollback.
  • Named owner if this causes problems: @shrugs

Pre-Review Checklist (Blocking)

  • I reviewed every line of this diff and understand it end-to-end
  • I'm prepared to defend this PR line-by-line in review
  • I'm comfortable being the on-call owner for this change
  • Relevant changesets are included (or explicitly not required)

Add a length-capped (64 code point) materialized `domains.__canonical_name_prefix`
column to back left-anchored/substring search and NAME ordering, replacing the
`left(canonical_name, 256)` expression index. Direct-SQL consumers can now
`WHERE __canonical_name_prefix LIKE 'vit%' ORDER BY __canonical_name_prefix`
without replicating the expression.

- ensdb-sdk: column (explicit DB name to preserve the `__`), shared
  CANONICAL_NAME_PREFIX_LENGTH + truncateCanonicalNamePrefix, repointed composite
  btree + GIN trigram onto the prefix column; hash on canonical_name kept for eq/in.
- ensindexer: maintain the column in all three canonicality write paths
  (ensureDomainInRegistry, cascadeCanonicality, cascadeLabelHeal).
- ensapi: Omnigraph name.starts_with + NAME order/cursor target the prefix column;
  canonical_name still returned everywhere and used for exact matches.
- docs: schema-reference column/index + prefix-search example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 4, 2026 18:19
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Jun 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
enskit-react-example.ensnode.io Ready Ready Preview, Comment Jun 4, 2026 8:16pm
ensnode.io Ready Ready Preview, Comment Jun 4, 2026 8:16pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
admin.ensnode.io Skipped Skipped Jun 4, 2026 8:16pm
ensrainbow.io Skipped Skipped Jun 4, 2026 8:16pm

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 4, 2026

🦋 Changeset detected

Latest commit: a52694f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 24 packages
Name Type
@ensnode/ensdb-sdk Patch
ensindexer Patch
ensapi Patch
@ensnode/integration-test-env Patch
@ensnode/ensnode-sdk Patch
ensadmin Patch
ensrainbow Patch
fallback-ensapi Patch
enssdk Patch
enscli Patch
enskit Patch
ensskills Patch
@ensnode/datasources Patch
@ensnode/ensrainbow-sdk Patch
@ensnode/ponder-sdk Patch
@ensnode/ponder-subgraph Patch
@ensnode/shared-configs Patch
@docs/ensnode Patch
@docs/ensrainbow Patch
@namehash/ens-referrals Patch
@namehash/namehash-ui Patch
@ensnode/ensindexer-perf-testing Patch
@ensnode/enskit-react-example Patch
@ensnode/enssdk-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

Warning

Review limit reached

@shrugs, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 33 minutes and 12 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 76e3f4dc-79af-44f4-83d3-b84898ae9e9d

📥 Commits

Reviewing files that changed from the base of the PR and between 78d9e32 and a52694f.

⛔ Files ignored due to path filters (1)
  • packages/enssdk/src/omnigraph/generated/schema.graphql is excluded by !**/generated/**
📒 Files selected for processing (8)
  • .changeset/searchable-canonical-name-prefix.md
  • apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver-helpers.ts
  • apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts
  • apps/ensapi/src/omnigraph-api/schema/domain-inputs.ts
  • apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx
  • packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts
📝 Walkthrough

Walkthrough

This PR adds a materialized domains.__canonical_name_prefix column (first 64 code points of canonical_name) to enable efficient left-anchored substring search and deterministic NAME ordering. The schema gains the column and updated indexes; the database layer maintains prefix materialization during canonicality updates; the API resolver filters and orders by prefix; and documentation guides usage.

Changes

Searchable canonical name prefix materialization

Layer / File(s) Summary
Schema definition and prefix utilities
packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts
Adds CANONICAL_NAME_PREFIX_LENGTH constant and truncateCanonicalNamePrefix helper, introduces __canonicalNamePrefix materialized column (null when canonical = false), and replaces prior left-truncated index expressions with new composite (registryId, __canonicalNamePrefix, id) and GIN trigram indexes on the prefix column.
Database materialization and canonicality updates
apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts
Updates ensureDomainInRegistry, cascadeLabelHeal, and cascadeCanonicality SQL to compute and persist __canonical_name_prefix alongside canonical_name; ensures prefix is materialized on first canonicalization, updated during label healing, and set/cleared during canonical flag transitions.
API resolver filtering and cursor ordering
apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver-helpers.ts, find-domains-resolver.ts
Removes local CANONICAL_NAME_SORT_PREFIX constant and delegates truncateNameForCursor to SDK; updates name.starts_with filter to perform ILIKE against __canonicalNamePrefix instead of full canonicalName; changes NAME cursor ordering to use materialized prefix column instead of computing left(...) expression.
Documentation and release notes
.changeset/searchable-canonical-name-prefix.md, docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx, domain-by-name.mdx
Updates schema reference to describe the new __canonical_name_prefix column and its indexes; adds tip section with example for prefix/typeahead searches; documents version bumps for SDK, indexer, and API packages.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A prefix so fine, now materialized with care,
Sixty-four code points, truncated with flair,
Search becomes speedy, no left() left behind,
The database whispers: this ordering's refined! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a materialized searchable __canonical_name_prefix column to support efficient prefix/typeahead searches.
Description check ✅ Passed The PR description comprehensively covers all template sections including summary, motivation, what changed, testing, and pre-review checklist. It goes well beyond minimum requirements with extensive detail on design decisions and risk mitigation.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-canonical-name-prefix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts`:
- Around line 68-70: The current prefix search only queries the materialized
__canonicalNamePrefix which is truncated to 64 code points and thus drops
matches when filter.starts_with is longer; change the logic in the
find-domains-resolver (where return
ilike(ensIndexerSchema.domain.__canonicalNamePrefix, `${filter.starts_with}%`)
is used) to handle long prefixes: if filter.starts_with length <= 64 keep the
existing ilike against __canonicalNamePrefix, otherwise run the starts-with
ilike against the full canonicalName (ensIndexerSchema.domain.canonicalName)
instead (or use a combined condition: prefix-ilike OR canonicalName-ilike) so
long inputs still match. Ensure you reference filter.starts_with,
__canonicalNamePrefix and canonicalName when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f2c6dd11-bfc6-46a0-89c0-0cd15c984e92

📥 Commits

Reviewing files that changed from the base of the PR and between 100dbce and 78d9e32.

📒 Files selected for processing (7)
  • .changeset/searchable-canonical-name-prefix.md
  • apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver-helpers.ts
  • apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts
  • apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx
  • packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts

Comment thread apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts Outdated
Comment thread .changeset/searchable-canonical-name-prefix.md Outdated
Comment thread apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver-helpers.ts Outdated
Comment thread apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver-helpers.ts Outdated
Comment thread apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts Outdated
Comment thread apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts Outdated
Comment thread docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx Outdated
Comment thread packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts Outdated
Comment thread packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts Outdated
- drop truncateNameForCursor wrapper; use truncateCanonicalNamePrefix directly
- remove now-redundant inline comments (starts_with, cursor NAME)
- rename cascadeLabelHeal CTE healed -> canonical_name
- early return in truncateCanonicalNamePrefix
- docs: reword search-vs-display tip; convert all Indexes sections to lists;
  document current state only (drop previous-approach framing)
- changeset: drop "Reindex required"

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shrugs
Copy link
Copy Markdown
Member Author

shrugs commented Jun 4, 2026

@greptile review

@shrugs shrugs marked this pull request as ready for review June 4, 2026 18:47
@shrugs shrugs requested a review from a team as a code owner June 4, 2026 18:47
Copilot AI review requested due to automatic review settings June 4, 2026 18:47
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 4, 2026

Greptile Summary

Materializes a new domains.__canonical_name_prefix column (first 64 code points of canonical_name, NULL when non-canonical) to replace the previous left(canonical_name, 256) expression index, giving ORDER BY and ILIKE searches a real, length-capped column to target instead of requiring every caller to replicate the expression.

  • All three write paths in canonicality-db-helpers.ts (ensureDomainInRegistry, cascadeCanonicality, cascadeLabelHeal) consistently maintain both canonical_name and __canonical_name_prefix; cascadeLabelHeal was refactored into a CTE so the string_agg name is computed once and used for both columns.
  • The explicit DB column name t.text(\"__canonical_name_prefix\") is load-bearing — ponder's default casing strips leading underscores, which would have silently broken the raw-SQL SET __canonical_name_prefix = ... clauses.
  • eq/in exact-match filters remain on the full canonical_name (hash-indexed); starts_with and NAME ordering now target __canonical_name_prefix; the 64-code-point cap is documented in the GraphQL schema description and in the updated schema-reference docs.

Confidence Score: 5/5

Safe to merge — all three write paths are consistent, the explicit column name correctly preserves the leading underscores, and exact-match correctness is unaffected.

Every place that sets canonical_name also sets __canonical_name_prefix (verified across all three helpers). The explicit t.text("__canonical_name_prefix") bypasses ponder's casing correctly. eq/in still target the full canonical_name column, so exact-match behaviour is unchanged. The cascadeLabelHeal CTE restructure is semantically equivalent to the previous correlated subquery. The 64-char cap is documented at the GraphQL layer. No correctness regressions were found.

No files require special attention.

Important Files Changed

Filename Overview
packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts Adds CANONICAL_NAME_PREFIX_LENGTH=64, truncateCanonicalNamePrefix(), and the __canonicalNamePrefix column with an explicit DB name to prevent ponder's casing from stripping the leading __; replaces the expression index on left(canonical_name,256) with a real column-backed composite btree and moves the GIN trigram onto the prefix column.
apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts All three write paths (ensureDomainInRegistry, cascadeCanonicality, cascadeLabelHeal) consistently write both canonical_name and __canonical_name_prefix; cascadeLabelHeal refactored from a correlated subquery into a CTE to compute the healed name once for both columns.
apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver-helpers.ts Removes CANONICAL_NAME_SORT_PREFIX and truncateNameForCursor; NAME order column is now the real __canonicalNamePrefix column reference instead of a left() expression; cursor filter is simplified.
apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts starts_with filter targets __canonicalNamePrefix; NAME cursor reads domain.__canonicalNamePrefix directly; eq/in remain on canonicalName.

Reviews (12): Last reviewed commit: "docs(unigraph): use ILIKE in prefix-sear..." | Re-trigger Greptile

Comment thread apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts Outdated
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 4, 2026

Greptile Summary

This PR materializes a new domains.__canonical_name_prefix column — the first 64 code points of canonical_name — to replace the previous left(canonical_name, 256) expression index for NAME ordering and typeahead search. The prefix column is maintained across all three write paths in canonicality-db-helpers.ts and kept off the public GraphQL API.

  • Schema: new __canonicalNamePrefix column with an explicit DB name (t.text(\"__canonical_name_prefix\")) to prevent Ponder's default casing from stripping the leading underscores; shared CANONICAL_NAME_PREFIX_LENGTH = 64 constant and truncateCanonicalNamePrefix() helper exported from ensdb-sdk and consumed by both indexer and API.
  • Indexes: composite btree (registry_id, __canonical_name_prefix, id) replaces the old expression index for ordered registry-scoped scans; GIN trigram moved from canonical_name to __canonical_name_prefix; hash index on canonical_name unchanged for exact matches.
  • API: starts_with filter now targets __canonicalNamePrefix; eq/in remain on canonicalName; NAME cursor encoding switches from 256-char to 64-char truncation, consistent with the new column.

Confidence Score: 4/5

Safe to merge; requires a full reindex, but the write-path changes are consistent and the public GraphQL schema is unchanged.

All three write paths correctly maintain __canonical_name_prefix alongside canonical_name. The explicit column name prevents Ponder's underscore-stripping from silently breaking the raw-SQL SET clauses. Exact-match queries (eq/in) still target the full canonical_name, and the cursor encoding is consistent with the new 64-char prefix column. The one concern worth noting is the cascadeLabelHeal CTE being named canonical_name — the same as the column it updates — which PostgreSQL resolves correctly but can mislead future readers of the query.

apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts — specifically the cascadeLabelHeal CTE naming.

Important Files Changed

Filename Overview
packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts Adds CANONICAL_NAME_PREFIX_LENGTH = 64, truncateCanonicalNamePrefix(), and the new __canonicalNamePrefix column with an explicit DB name to prevent Ponder's leading-underscore stripping. Indexes are correctly re-targeted: composite btree on (registry_id, __canonical_name_prefix, id) for NAME ordering, GIN trigram on __canonical_name_prefix for fuzzy search, hash on full canonical_name for exact matches.
apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts All three write paths correctly maintain __canonical_name_prefix alongside canonical_name: JS-side ensureDomainInRegistry calls truncateCanonicalNamePrefix, the SQL CTE in cascadeCanonicality uses left(dt.new_name, 64), and the restructured cascadeLabelHeal CTE computes the name once and assigns left(canonical_name.name, 64). The CTE in cascadeLabelHeal is named canonical_name, which matches the column being updated — technically valid but a potential clarity trap.
apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver.ts starts_with now targets __canonicalNamePrefix; eq/in remain on canonicalName. Cursor encoding switched from local truncateNameForCursor (256-char) to shared truncateCanonicalNamePrefix (64-char), consistent with the new prefix column length.
apps/ensapi/src/omnigraph-api/lib/find-domains/find-domains-resolver-helpers.ts Removes the now-unnecessary local CANONICAL_NAME_SORT_PREFIX / truncateNameForCursor pair; NAME order column changed from left(canonical_name, 256) expression to the materialized __canonicalNamePrefix column. Cursor filter logic is unchanged — it still compares the cursor value (now 64-char prefix) directly against the order column.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Domain write event] --> B{Write path}

    B -->|ensureDomainInRegistry JS-side Drizzle update| C["truncateCanonicalNamePrefix(canonicalName) → __canonicalNamePrefix"]
    B -->|cascadeCanonicality SQL CTE| D["left(dt.new_name, 64) → __canonical_name_prefix NULL when canonical=false"]
    B -->|cascadeLabelHeal SQL CTE restructured| E["WITH canonical_name AS (...) left(canonical_name.name, 64) → __canonical_name_prefix"]

    C & D & E --> F[(domains table canonical_name full text __canonical_name_prefix first 64 cp)]

    F --> G{Query type}
    G -->|starts_with| H["ilike(__canonical_name_prefix, 'vit%') GIN trigram index"]
    G -->|eq / in| I["= / IN on canonical_name hash index"]
    G -->|NAME ORDER BY| J["ORDER BY __canonical_name_prefix btree index registry_id, prefix, id"]
Loading

Reviews (2): Last reviewed commit: "fix(unigraph): address review feedback (..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts
Comment thread apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts Outdated
- NAME cursor reads stored domain.__canonicalNamePrefix directly (single
  source of truth) instead of recomputing from canonicalName; drop the
  now-unused truncateCanonicalNamePrefix import in the resolver
- document the 64-code-point starts_with cap in the GraphQL field
  description; regenerate Omnigraph SDL

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shrugs
Copy link
Copy Markdown
Member Author

shrugs commented Jun 4, 2026

@greptile review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

the GIN trigram backs the LIKE filter; ORDER BY __canonical_name_prefix
sorts the matched set unless scoped by registry_id (composite btree).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io June 4, 2026 19:11 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io June 4, 2026 19:11 Inactive
@shrugs
Copy link
Copy Markdown
Member Author

shrugs commented Jun 4, 2026

@greptile review

Comment thread apps/ensindexer/src/lib/ensv2/canonicality-db-helpers.ts Outdated
Comment thread packages/ensdb-sdk/src/ensindexer-abstract/unigraph.schema.ts
…loop 5)

avoids the CTE name shadowing the target canonical_name column in the
UPDATE...SET, which several reviewers found ambiguous.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 4, 2026 20:05
@vercel vercel Bot temporarily deployed to Preview – ensnode.io June 4, 2026 20:06 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io June 4, 2026 20:06 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io June 4, 2026 20:06 Inactive
@shrugs
Copy link
Copy Markdown
Member Author

shrugs commented Jun 4, 2026

@greptile review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 5 comments.

Comment thread docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx Outdated
Comment thread docs/ensnode.io/src/content/docs/docs/integrate/unigraph/schema-reference.mdx Outdated
align direct-SQL prefix-search docs with the case-insensitive Omnigraph
starts_with semantics; the GIN trigram index serves ILIKE equally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shrugs
Copy link
Copy Markdown
Member Author

shrugs commented Jun 4, 2026

@greptile review

Copy link
Copy Markdown
Member

@tk-o tk-o left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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.

3 participants