Skip to content

feat(search): match Elasticsearch token semantics exactly (PPT-2644) - #446

Merged
camreeves merged 2 commits into
masterfrom
PPT-2644-or-token-joining
Aug 13, 2026
Merged

feat(search): match Elasticsearch token semantics exactly (PPT-2644)#446
camreeves merged 2 commits into
masterfrom
PPT-2644-or-token-joining

Conversation

@camreeves

@camreeves camreeves commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Part of PPT-2644. Follow-up to #445, restoring exact Elasticsearch token semantics for multi-word q to avoid behavioral regressions against the pre-migration platform.

What changes

  • Token joining: AND → OR. feat: replace Elasticsearch with PostgreSQL full-text search (PPT-2644, 2/2) #445 shipped AND-of-prefixes (every term required). This returns to what ES's simple_query_string did: a record matches when any term matches.
  • Prefix scope: last token only. ES appended * to the query's final token; earlier terms were whole-word matches. Now identical. (sydney roo = whole-word sydney OR prefix roo — the term being typed still autocompletes.)
  • Email precision preserved. Under pure OR, q=<address> would OR-split and match every user on the domain. Old clients had precise email lookup via the (now accepted-and-ignored) fields=email phrase path, so email-shaped input is kept as a single whole-address lexeme — the vectors index addresses both whole and tokenized for exactly this.
  • New dedicated Utils::TextSearch spec (nine cases: the never-500 vectors, ES-syntax degradation, email handling, token/length caps, unicode).

Notes for reviewers

  • Multi-word searches will return more results than feat: replace Elasticsearch with PostgreSQL full-text search (PPT-2644, 2/2) #445's AND behavior (and identical-or-slightly-more than old ES, since nothing else narrowed) — X-Total-Count grows accordingly. This is the intended ES parity.
  • The sanitizer's never-500 guarantee is unchanged: ES operator syntax, field prefixes, quotes and junk all degrade to plain terms.
  • Full containerised suite: 885 examples, 0 failures, 0 errors (1 pre-existing pending).

Multi-word queries are now OR-joined whole-word matches with a prefix on
the final token only — precisely what the old simple_query_string plus
Neuroplastic's trailing * produced — replacing the AND-of-prefixes the
initial migration shipped. Email-shaped input stays a single quoted
whole-address lexeme so user lookup keeps the precision the old
field-scoped phrase match provided (OR-splitting an address would match
everyone on its domain). Adds the previously-missing dedicated sanitizer
spec (9 cases incl. the never-500 vectors).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the type: enhancement new feature or request label Aug 13, 2026
@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR restores Elasticsearch-compatible token semantics for PostgreSQL text search while replacing placeholder-based email handling with collision-free tokenization.

  • OR-joins search tokens and applies prefix matching only to the final token.
  • Preserves email addresses as whole quoted lexemes.
  • Adds coverage for sanitization, Unicode, limits, email handling, and the previously reported placeholder collision.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/placeos-rest-api/utilities/text-search.cr Restores OR-based Elasticsearch token behavior and replaces collision-prone email placeholders with ordered single-pass tokenization.
spec/text_search_spec.cr Adds focused coverage for token joining, final-token prefixes, syntax degradation, emails, collision handling, Unicode, and resource caps.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Free-form query] --> B[Cap input length]
    B --> C[Single-pass tokenizer]
    C --> D{Token type}
    D -->|Email| E[Quoted whole-address lexeme]
    D -->|Field prefix| F[Discard]
    D -->|Operator| G[Discard]
    D -->|Plain word| H[Word lexeme]
    E --> I[Cap token count]
    H --> I
    I --> J[OR-join tokens]
    J --> K[Prefix final token only]
Loading

Reviews (2): Last reviewed commit: "refactor(search): tokenize in a single o..." | Re-trigger Greptile

Comment thread src/placeos-rest-api/utilities/text-search.cr Outdated
Replaces the email placeholder substitution with one scan that captures
addresses, consumes field: prefixes and collects words in position order.
Eliminates the placeholder-collision edge Greptile flagged (a literal
'placeosemailtokenNx' alongside an email hijacked the mapping) and the
latent email-followed-by-colon interaction — both now spec-pinned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added type: enhancement new feature or request and removed type: enhancement new feature or request labels Aug 13, 2026
@camreeves
camreeves merged commit 400c1a5 into master Aug 13, 2026
16 of 17 checks passed
@camreeves
camreeves deleted the PPT-2644-or-token-joining branch August 13, 2026 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: enhancement new feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant