Skip to content

fix(persons): search WhatsApp participant identities#696

Merged
namastex888 merged 1 commit into
devfrom
fix/persons-search-whatsapp-identities-dev
Jun 2, 2026
Merged

fix(persons): search WhatsApp participant identities#696
namastex888 merged 1 commit into
devfrom
fix/persons-search-whatsapp-identities-dev

Conversation

@namastex888
Copy link
Copy Markdown
Contributor

Summary

  • Expands PersonService.search() beyond sparse persons rows to include platform identities and WhatsApp chat_participants names/ids.
  • Preserves the actual matched participant/identity name in results, so LID DMs like Cadu Cassau are discoverable.
  • Handles literal backslashes in search input via ESCAPE ''.
  • Fixes CLI rendering to read primaryEmail/primaryPhone from SDK/API person objects.
  • Adds a regression test for a contact whose WhatsApp name only exists on chat participants.

Dogfood / proof

  • Live local Omni DB repair aligned the identity graph and backfilled missing names.
  • Verified across all distinct non-Felipe named WhatsApp participants in the live DB:
    • tested_known_names = 835
    • found = 835
    • missing = 0
  • CLI/API spot checks pass for known high-volume contacts:
    • Stéfani
    • Cezar Augusto
    • Raphael Rosa
    • Yolanda Karolina
    • Cadu Cassau
    • literal backslash name /\/\/\/

Validation

  • bun test packages/api/src/services/__tests__/persons.test.ts → 11 pass / 0 fail
  • bun run --filter @omni/api typecheck → pass
  • bun run --filter @automagik/omni build → pass
  • bun run --filter @automagik/omni build:server → pass
  • pre-push full gates on this dev branch → 3971 pass / 292 skip / 0 fail

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 60d061f0-f04e-4958-af24-5ebc80573fa5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/persons-search-whatsapp-identities-dev

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

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the PersonService.search method to use a raw SQL query that searches across persons, platform_identities, and chat_participants to improve search results, particularly for WhatsApp contacts. It also updates the CLI command to support the new schema fields and adds corresponding unit tests. Feedback on the changes highlights two main issues: first, the use of DISTINCT ON (id) with LIMIT causes the results to be ordered arbitrarily by UUID, which can discard relevant matches; second, a portion of the UNION ALL query is dead code due to database foreign key constraints that prevent cp.person_id from referencing a non-existent person.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/api/src/services/persons.ts Outdated
Comment on lines +147 to +158
SELECT DISTINCT ON (id)
id,
"displayName",
"primaryPhone",
"primaryEmail",
"avatarUrl",
metadata,
"createdAt",
"updatedAt"
FROM candidates
ORDER BY id, rank_ts DESC
LIMIT ${limit}
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.

high

When using DISTINCT ON (id) in PostgreSQL, the query requires the first expression in ORDER BY to match the DISTINCT ON expression (which is id here).

Because of this, the final results are sorted by id (which is a UUID and effectively random) before the LIMIT is applied. This means the LIMIT ${limit} will discard highly relevant (recently active) matches in favor of arbitrary UUID ordering, and the returned list will not be sorted by rank_ts DESC.

To fix this and ensure the most relevant/recently active results are returned, you should wrap the DISTINCT ON query in a subquery or CTE, and then perform a final ORDER BY rank_ts DESC LIMIT ${limit} on the distinct results.

      SELECT
        id,
        "displayName",
        "primaryPhone",
        "primaryEmail",
        "avatarUrl",
        metadata,
        "createdAt",
        "updatedAt"
      FROM (
        SELECT DISTINCT ON (id)
          id,
          "displayName",
          "primaryPhone",
          "primaryEmail",
          "avatarUrl",
          metadata,
          "createdAt",
          "updatedAt",
          rank_ts
        FROM candidates
        ORDER BY id, rank_ts DESC
      ) distinct_candidates
      ORDER BY rank_ts DESC
      LIMIT ${limit}

Comment thread packages/api/src/services/persons.ts Outdated
Comment on lines +121 to +145
UNION ALL

SELECT
cp.person_id AS id,
cp.display_name AS "displayName",
NULL::text AS "primaryPhone",
NULL::text AS "primaryEmail",
cp.avatar_url AS "avatarUrl",
jsonb_build_object(
'source', 'chat_participants',
'chatId', cp.chat_id,
'platformUserId', cp.platform_user_id
) AS metadata,
cp.created_at AS "createdAt",
cp.updated_at AS "updatedAt",
COALESCE(cp.last_seen_at, cp.updated_at, cp.created_at, 'epoch'::timestamptz) AS rank_ts
FROM chat_participants cp
LEFT JOIN persons p ON p.id = cp.person_id
WHERE
cp.person_id IS NOT NULL
AND p.id IS NULL
AND (
cp.display_name ILIKE ${searchPattern} ESCAPE ''
OR cp.platform_user_id ILIKE ${searchPattern} ESCAPE ''
)
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.

medium

The UNION ALL block attempts to query chat_participants where cp.person_id IS NOT NULL AND p.id IS NULL.

However, in the database schema (packages/db/src/schema.ts), chat_participants.personId has a foreign key constraint referencing persons.id with onDelete: 'set null':
personId: uuid('person_id').references(() => persons.id, { onDelete: 'set null' })

Because of this referential integrity, p.id can never be NULL when cp.person_id is not null. If a referenced Person is deleted, the database automatically sets cp.person_id to NULL. Consequently, this entire UNION ALL block is dead code and will always return zero rows. You should remove this dead UNION ALL block to simplify the query and improve performance.

@namastex888 namastex888 force-pushed the fix/persons-search-whatsapp-identities-dev branch from ae5b864 to 8c95674 Compare June 2, 2026 17:29
@namastex888
Copy link
Copy Markdown
Contributor Author

Reviewed Gemini stale comments: both made sense and are handled in the latest force-push. The contradictory UNION ALL branch is gone, and DISTINCT ON is now inside distinct_candidates; final recency sort + limit happens outside. Local/pre-push validation passed.

@namastex888 namastex888 merged commit d71ebf5 into dev Jun 2, 2026
10 checks passed
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