Skip to content

fix(search): normalize profile name tiers in one place - #8334

Merged
loganj merged 1 commit into
mainfrom
larry/profile-search-tier-normalization
Oct 15, 2026
Merged

loganj merged 1 commit into
mainfrom
larry/profile-search-tier-normalization

Conversation

@loganj

@loganj loganj commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

🤖

Summary

Profile name search (POST /profiles/search, added in #8301) sorts results into tiers: an exact name first, then a name that starts with the query, then a name with a later word that starts with it. This PR stacks on #8301 and fixes two review items.

Tier normalization in one place. Before this change, the query was lower-cased in Rust (to_lowercase), and the names were lower-cased in SQL (lower()). These can disagree. Under a C collation, PostgreSQL's lower() changes only ASCII letters, while Rust also lower-cases letters like É. So a search for Émile did not put the profile named Émile in the exact tier. Now the query and both name fields go through the same SQL expression: lower-case, collapse whitespace runs to one space, then trim. The query is bound once and both sides compare equal by construction. The trim also moved after the collapse, so surrounding whitespace that the regex turns into a space is now trimmed too.

Ranking wording. The doc comment and docs/profile-search.md said "ties keep the NIP-50 order". That was wrong. Ties are ordered by ts_rank_cd over the two name fields only (not the whole profile, as in NIP-50), then newest first, then event id.

Out of scope, not changed here: matching names that contain escaped Unicode or newlines, and any new name index.

Related issue

Review on #8301: #8301 (review)

Testing

  • New case in profile_names_match_name_words_only: profiles Émile and a newer Émilee, query Émile. The exact match must come first.
  • Ran the Postgres integration tests against a postgres:17-alpine database created with --locale=C:
  • cargo fmt --check, cargo clippy -p buzz-search --all-targets -D warnings, and cargo test -p buzz-search: clean.
  • The pre-push hook's rust-tests and desktop-tauri-checks passed.

@loganj
loganj force-pushed the larry/profile-name-search branch from fd3b612 to 885dc57 Compare October 14, 2026 22:27
@loganj
loganj force-pushed the larry/profile-search-tier-normalization branch from dc73936 to 560b12b Compare October 14, 2026 22:27
@loganj
loganj force-pushed the larry/profile-name-search branch from 885dc57 to a2e2a24 Compare October 14, 2026 23:35
@loganj
loganj force-pushed the larry/profile-search-tier-normalization branch 2 times, most recently from dce8039 to 82a9cbd Compare October 15, 2026 20:10
@loganj
loganj force-pushed the larry/profile-name-search branch from 958ba50 to 51320bf Compare October 15, 2026 20:10
@loganj
loganj marked this pull request as ready for review October 15, 2026 20:33
@loganj
loganj requested a review from a team as a code owner October 15, 2026 20:34
@loganj
loganj force-pushed the larry/profile-name-search branch from 51320bf to 4bc1ec0 Compare October 15, 2026 20:40
@loganj
loganj force-pushed the larry/profile-search-tier-normalization branch from 82a9cbd to 3578814 Compare October 15, 2026 20:40
jedwards27
jedwards27 previously approved these changes Oct 15, 2026

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:bot: Jude’s code review agent

APPROVE at exact head 35788145b3c84c49b1029986cfaca9ae32a6d7fa over stacked base 4bc1ec060d38920097f3ade6c399361f93faa319.

No author-actionable defect found.

The three-file patch removes the Rust/PostgreSQL case-normalization split by constructing the query and both name-tier operands from the same bound SQL normalizer (btrim(regexp_replace(lower(...), '\s+', ' ', 'g'))). Candidate matching and tenant/current-profile/name-only fences are unchanged. Ordering remains total and page-stable: tier, names-only ts_rank_cd, newest first, then event id. The API docs now describe that actual tie order.

The added PostgreSQL regression is behaviorally relevant: exact Émile must precede newer prefix Émilee, exposing the previous Rust to_lowercase versus database lower() disagreement. cargo fmt --all -- --check and git diff --check passed on a clean tree at the reviewed head. Exact-head replacement CI has Rust lint, unit tests, PostgreSQL tests, relay/backend integration, security, and Linux cross-compiles green with no active-run failure at submission.

The apparent failed fan-out belongs to cancelled superseded run 38803916833; its selector was cancelled and downstream jobs terminated within seconds. Replacement run 38803918923 is authoritative.

Author action: none.

Confidence gaps / verification owner: local Rust execution was blocked before compilation by the reviewer host's unaccepted Xcode 27 license (cc exit 69); reviewer/tooling owns that environment issue. A few unrelated exact-head CI lanes were still running at submission; CI owns merge readiness. Any head movement expires this approval.

Base automatically changed from larry/profile-name-search to main October 15, 2026 21:35
@loganj
loganj dismissed jedwards27’s stale review October 15, 2026 21:35

The base branch was changed.

Compare the query and the profile names after the same SQL normalization
(lower, whitespace runs collapsed, trimmed). Rust to_lowercase and the
database lower() disagreed for non-ASCII letters under a C collation, so an
exact name such as Émile missed tier 0.

Describe the real tie order: name relevance, newest first, event id.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <[email protected]>
@loganj
loganj force-pushed the larry/profile-search-tier-normalization branch from 3578814 to 1df4575 Compare October 15, 2026 21:37
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Oct 15, 2026
@github-actions

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: f148c0f22c35105524527dec8e4a7285c26804c6...1df457592b22961c5e95c7ec0eacf9e104c6c20d
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: NONE

No concrete security, correctness, or reliability issues found in the authorized PR range. The ranking normalization preserves parameter binding, community scoping, authentication, and result visibility checks.

Findings

No concrete security, correctness, or reliability findings were identified.

Notes

  • Review used read-only source inspection. Builds and tests were not executed, as requested.

Generated by Codex Security Review |
Requested by: @loganj |
Workflow run

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:bot: Jude’s code review agent

Verdict: APPROVE
Reviewed: f148c0f22c35105524527dec8e4a7285c26804c6..1df457592b22961c5e95c7ec0eacf9e104c6c20d (exact head 1df457592b22961c5e95c7ec0eacf9e104c6c20d)
Risk: medium — contained server-side profile-search ranking and normalization behavior.

Behavior/contracts traced: The bound query, name, and display_name tier operands use the same PostgreSQL lowercase/whitespace normalizer in crates/buzz-search/src/query.rs:359-365,403-446. Community, profile-kind/current-profile, deletion, visibility, and non-null-name fences remain intact. Ordering stays total and page-stable: match tier, names-only ts_rank_cd, created_at DESC, then event ID. docs/profile-search.md:32-49 accurately limits relevance to name fields and does not promise accent folding or locale-independent Unicode casing. The regression at crates/buzz-search/tests/postgres_fts_integration.rs:1733-1740 exercises the production SQL seam with Émile versus newer prefix Émilee; the documented broken control reverses the intended order.

Findings: No blocking or non-blocking code defects found. The rebased commit has the same stable patch ID (15f0eb006ba123d2f4ca93697b724dbda50dab95) and byte-identical touched-path blobs as previously reviewed 35788145b3c84c49b1029986cfaca9ae32a6d7fa; relevant base objects are unchanged, while the base-only addition is an unrelated mobile accessibility script.

Author action: none.
Verification owner: exact-head CI / merge gate for jobs still running; reviewer-machine operator for local Xcode/Docker availability.

Validation at matching HEAD: clean checkout at 1df457592b22961c5e95c7ec0eacf9e104c6c20d; git diff --check f148c0f2...HEAD passed; . ./bin/activate-hermit && cargo fmt --all -- --check passed. GitHub reported the PR open, non-draft, and mergeable at the pinned base/head. Exact-head CI had 40 passing checks and no failures at review submission, including Rust lint/unit, relay artifact PostgreSQL tests, backend/relay integration, security, macOS, Linux cross-compiles, and image builds.

Manual/native evidence: not applicable to this server-side SQL ranking change. Independent local cargo test -p buzz-search/clippy execution was blocked by an unaccepted Xcode license, and Docker/PostgreSQL was unavailable on the reviewer host; these are reviewer-tooling gaps, not PR-caused failures.

Residual risk: low. Windows Rust, the PostgreSQL domain test, and three downstream Desktop integration shards were still running with no red exact-head job. Those external gates—not author rework—own final merge readiness.

@loganj
loganj merged commit 7852b16 into main Oct 15, 2026
84 checks passed
@loganj
loganj deleted the larry/profile-search-tier-normalization branch October 15, 2026 22:02
wpfleger96 pushed a commit that referenced this pull request Oct 16, 2026
…i-bindings

* origin/main:
  feat(mobile): find people by name through the relay's name-only profile search (#8333)
  fix(ios): respect app minimum in generated CocoaPods targets (#8379)
  fix(relay): confirm requested channels in one membership query (#8378)
  fix(mobile): split channel history batches at the relay channel cap (#8342)
  fix(search): normalize profile name tiers in one place (#8334)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
wpfleger96 pushed a commit that referenced this pull request Oct 16, 2026
… into duncan/bbpublic-nip-fi-resolver

* origin/duncan/bbpublic-nip-fi-bindings:
  feat(mobile): find people by name through the relay's name-only profile search (#8333)
  fix(ios): respect app minimum in generated CocoaPods targets (#8379)
  fix(relay): confirm requested channels in one membership query (#8378)
  fix(mobile): split channel history batches at the relay channel cap (#8342)
  fix(search): normalize profile name tiers in one place (#8334)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>

This branch was previously deployed

1 inactive deployment
codex-review — 1df45759 Deployed Oct 15, 2026 by loganj via Run Codex Security Review #8392
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants