Skip to content

fix: de-duplicate events returned by generic tag-filter subscriptions#694

Open
Priyanshubhartistm wants to merge 3 commits into
cameri:mainfrom
Priyanshubhartistm:fix/duplicate-events-on-tag-filter-queries
Open

fix: de-duplicate events returned by generic tag-filter subscriptions#694
Priyanshubhartistm wants to merge 3 commits into
cameri:mainfrom
Priyanshubhartistm:fix/duplicate-events-on-tag-filter-queries

Conversation

@Priyanshubhartistm

Copy link
Copy Markdown
Collaborator

Description

EventRepository.findByFilters() left-joins event_tags when a filter contains a generic tag query (#e, #p, #g, etc.) and selects events.* with no DISTINCT or GROUP BY. If an event has more than one event_tags row matching the filter's criteria for the same query - for example a filter of {"#p": ["a", "b"]} matching an event that is tagged with both a and b - the join produces one result row per matching tag row instead of one row per event. Those rows are streamed straight to the subscriber by SubscribeMessageHandler.fetchAndSend(), so the client receives duplicate EVENT messages for the same event within a single subscription.

The tag-query branch of findByFilters() now uses builder.distinct('events.*') instead of builder.select('events.*'), so each stored event is returned at most once regardless of how many tag rows it matches. countByFilters() was already unaffected - it already wraps its result in countDistinct({ count: 'event_id' }).

Related Issue

#693

Motivation and Context

This is a protocol-correctness bug: NIP-01 subscribers expect each matching event once per subscription. Duplicate delivery wastes client bandwidth/processing and can confuse client-side dedup logic that isn't expecting repeats within the same REQ.

How Has This Been Tested?

  • Updated the 11 existing unit tests in test/unit/repositories/event-repository.spec.ts that assert the exact generated SQL for #e, #g, #p, #r, and #d tag filters - they now expect select distinct "events".* instead of select "events".*.
  • Ran the full test/unit/repositories/event-repository.spec.ts suite (62 tests) - all pass, including the countByFilters tests, confirming that code path (already correct via countDistinct) is untouched.
  • Ran pnpm lint, pnpm check:deps, pnpm run build, pnpm run build:check, pnpm run verify:cli:build, pnpm run test:unit (1393 passing), pnpm run test:cli (73 passing), and pnpm run cover:unit - all pass.

Screenshots (if appropriate):

N/A - backend query change only.

Types of changes

  • Non-functional change (docs, style, minor refactor)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my code changes.
  • I added a changeset, or this is docs-only and I added an empty changeset.
  • All new and existing tests passed.

@changeset-bot

changeset-bot Bot commented Jul 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8b94b43

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

This PR includes changesets to release 1 package
Name Type
nostream 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

@coveralls

coveralls commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Coverage Status

coverage: 68.708% (+0.008%) from 68.7% — Priyanshubhartistm:fix/duplicate-events-on-tag-filter-queries into cameri:main

@cameri
cameri requested a review from Copilot July 20, 2026 18:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a protocol-correctness bug where generic tag-filter subscriptions could deliver the same stored event multiple times due to events being left-joined to event_tags without de-duplication.

Changes:

  • Switch tag-query findByFilters() selection to SELECT DISTINCT events.* to prevent join row-multiplication from producing duplicate events.
  • Update unit tests that assert generated SQL for generic tag filters to expect select distinct "events".*.
  • Add a changeset documenting the patch fix.

Reviewed changes

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

File Description
src/repositories/event-repository.ts Applies DISTINCT for tag-filtered (non-search) queries to prevent duplicate event rows.
test/unit/repositories/event-repository.spec.ts Updates SQL-string expectations for #e/#g/#p/#r/#d tag-filter tests to include DISTINCT.
.changeset/duplicate-events-on-tag-filter-queries.md Documents the bug and the de-duplication fix as a patch release.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/repositories/event-repository.ts
Comment thread test/unit/repositories/event-repository.spec.ts
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.

4 participants