Skip to content

Fix duplicate .sorted() call causing double joins on bookmarks page#1322

Merged
jmilljr24 merged 1 commit intomainfrom
maebeale/fix-bookmark-duplicate-join
Mar 4, 2026
Merged

Fix duplicate .sorted() call causing double joins on bookmarks page#1322
jmilljr24 merged 1 commit intomainfrom
maebeale/fix-bookmark-duplicate-join

Conversation

@maebeale
Copy link
Collaborator

@maebeale maebeale commented Mar 3, 2026

What is the goal of this PR and why is this important?

  • Fixes a production Honeybadger crash: ActiveRecord::StatementInvalid: Trilogy::ProtocolError: 1066: Not unique table/alias: 'community_news' on BookmarksController#personal
  • Bookmark.search already calls .sorted() internally, but both controller actions were calling .sorted() again on the result, doubling the SQL joins

How did you approach the change?

  • Removed duplicate .sorted() calls from BookmarksController#index and #personalBookmark.search already calls .sorted() internally, so calling it again joined the aliased tables a second time
  • Fixed existing model specs that replicated the same double-sort bug pattern
  • Added model specs covering sort + filter combos (title filter + title/newest/popularity sort, user-scoped + title sort)
  • Added request specs for personal and global index with sort=title and title= filter params

UI Testing Checklist

  • Verify bookmarks personal page loads without error
  • Verify bookmarks personal page with sort=title works
  • Verify bookmarks global index (admin) with sort=title works
  • Verify bookmarks global index with title search + sort=title works

Anything else to add?

  • The aliased table approach (st_ prefix in sort_by_title, wt_ prefix in windows_type) already on main prevents join conflicts between filter and sort scopes — this PR just removes the redundant second .sorted() call that duplicated the aliased joins

🤖 Generated with Claude Code

@maebeale maebeale requested a review from jmilljr24 March 3, 2026 16:29
Copy link
Collaborator

@jmilljr24 jmilljr24 left a comment

Choose a reason for hiding this comment

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

I'm still getting an error on this when combining a title with windows type.

Image

@jmilljr24
Copy link
Collaborator

Sending status to systemd every 1.0 sec
Use Ctrl-C to stop
Started GET "/bookmarks/personal?title=foster&user_id=&bookmarkable_type=&windows_type=Combined&sort=newest" for 127.0.0.1 at 2026-03-03 12:17:19 -0500
  ActiveRecord::SchemaMigration Load (0.6ms)  SELECT `schema_migrations`.`version` FROM `schema_migrations` ORDER BY `schema_migrations`.`version` ASC
Processing by BookmarksController#personal as HTML
  Parameters: {"title" => "foster", "user_id" => "", "bookmarkable_type" => "", "windows_type" => "Combined", "sort" => "newest"}
  User Load (0.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 9478 ORDER BY `users`.`id` ASC LIMIT 1
  ↳ app/controllers/application_controller.rb:48:in 'ApplicationController#authenticate_user!'
  Bookmark Load (0.8ms)  SELECT `bookmarks`.* FROM `bookmarks` WHERE `bookmarks`.`user_id` = 9478
  ↳ app/controllers/application_controller.rb:72:in 'ApplicationController#preload_current_user_associations'
  Person Load (0.8ms)  SELECT `people`.* FROM `people` WHERE `people`.`id` = 1 LIMIT 1
  ↳ app/controllers/application_controller.rb:74:in 'ApplicationController#preload_current_user_associations'
  Affiliation Load (0.7ms)  SELECT `affiliations`.* FROM `affiliations` WHERE `affiliations`.`person_id` = 1 AND `affiliations`.`inactive` = FALSE AND (end_date IS NULL OR end_date >= '2026-03-03')
  ↳ app/controllers/application_controller.rb:79:in 'ApplicationController#preload_current_user_associations'
  Bookmark Load (1.3ms)  SELECT `bookmarks`.* FROM `bookmarks` LEFT JOIN community_news      ON community_news.id      = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'CommunityNews'
LEFT JOIN events              ON events.id              = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Event'
LEFT JOIN organizations       ON organizations.id       = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Organization'
LEFT JOIN people              ON people.id              = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Person'
LEFT JOIN resources           ON resources.id           = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Resource'
LEFT JOIN stories             ON stories.id             = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Story'
LEFT JOIN story_ideas         ON story_ideas.id         = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'StoryIdea'
LEFT JOIN workshops           ON workshops.id           = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Workshop'
LEFT JOIN workshop_ideas      ON workshop_ideas.id      = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopIdea'
LEFT JOIN workshop_logs       ON workshop_logs.id       = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopLog'
LEFT JOIN workshop_variations ON workshop_variations.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopVariation' WHERE `bookmarks`.`user_id` = 9478 AND (community_news.title
       people.last_name LIKE '%foster%' OR organizations.name LIKE '%foster%' OR resources.title LIKE '%foster%' OR
       stories.title LIKE '%foster%' OR workshops.title LIKE '%foster%' OR workshop_ideas.title LIKE '%foster%' OR
       story_ideas.body LIKE '%foster%' OR
       DATE_FORMAT(workshop_logs.date, '%Y-%m-%d') LIKE '%foster%' OR
       workshop_variations.name LIKE '%foster%') AND (`bookmarks`.`id` IN (SELECT `bookmarks`.`id` FROM `bookmarks` LEFT JOIN community_news      ON community_news.id      = bookmarks.bookmarkable_i
LEFT JOIN events              ON events.id              = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Event'
LEFT JOIN organizations       ON organizations.id       = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Organization'
LEFT JOIN people              ON people.id              = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Person'
LEFT JOIN resources           ON resources.id           = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Resource'
LEFT JOIN stories             ON stories.id             = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Story'
LEFT JOIN story_ideas         ON story_ideas.id         = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'StoryIdea'
LEFT JOIN workshops           ON workshops.id           = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Workshop'
LEFT JOIN workshop_ideas      ON workshop_ideas.id      = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopIdea'
LEFT JOIN workshop_logs       ON workshop_logs.id       = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopLog'
LEFT JOIN workshop_variations ON workshop_variations.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopVariation' INNER JOIN resources
  ON resources.id = bookmarks.bookmarkable_id
 AND bookmarks.bookmarkable_type = 'Resource'
INNER JOIN windows_types
  ON windows_types.id = resources.windows_type_id
  AND resources.windows_type_id IS NOT NULL WHERE `bookmarks`.`user_id` = 9478 AND (community_news.title LIKE '%foster%' OR events.title LIKE '%foster%' OR people.first_name LIKE '%foster%' OR
       people.last_name LIKE '%foster%' OR organizations.name LIKE '%foster%' OR resources.title LIKE '%foster%' OR
       stories.title LIKE '%foster%' OR workshops.title LIKE '%foster%' OR workshop_ideas.title LIKE '%foster%' OR
       story_ideas.body LIKE '%foster%' OR
       DATE_FORMAT(workshop_logs.date, '%Y-%m-%d') LIKE '%foster%' OR
       workshop_variations.name LIKE '%foster%') AND (windows_types.name LIKE '%COMBINED%')) OR `bookmarks`.`id` IN (SELECT `bookmarks`.`id` FROM `bookmarks` LEFT JOIN community_news      ON community_news.id      = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'CommunityNews'
LEFT JOIN events              ON events.id              = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Event'
LEFT JOIN organizations       ON organizations.id       = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Organization'
LEFT JOIN people              ON people.id              = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Person'
LEFT JOIN resources           ON resources.id           = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Resource'
LEFT JOIN stories             ON stories.id             = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Story'
LEFT JOIN story_ideas         ON story_ideas.id         = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'StoryIdea'
LEFT JOIN workshops           ON workshops.id           = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Workshop'
LEFT JOIN workshop_ideas      ON workshop_ideas.id      = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopIdea'
LEFT JOIN workshop_logs       ON workshop_logs.id       = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopLog'
LEFT JOIN workshop_variations ON workshop_variations.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'WorkshopVariation' LEFT JOIN workshops
  ON workshops.id = bookmarks.bookmarkable_id
 AND bookmarks.bookmarkable_type = 'Workshop'
LEFT JOIN windows_types
  ON windows_types.id = workshops.windows_type_id
  AND workshops.windows_type_id IS NOT NULL WHERE `bookmarks`.`user_id` = 9478 AND (community_news.title LIKE '%foster%' OR events.title LIKE '%foster%' OR people.first_name LIKE '%foster%' OR
       people.last_name LIKE '%foster%' OR organizations.name LIKE '%foster%' OR resources.title LIKE '%foster%' OR
       stories.title LIKE '%foster%' OR workshops.title LIKE '%foster%' OR workshop_ideas.title LIKE '%foster%' OR
       story_ideas.body LIKE '%foster%' OR
       DATE_FORMAT(workshop_logs.date, '%Y-%m-%d') LIKE '%foster%' OR
       workshop_variations.name LIKE '%foster%') AND (windows_types.name LIKE '%COMBINED%'))) ORDER BY `bookmarks`.`created_at` DESC
  ↳ app/controllers/bookmarks_controller.rb:31:in 'BookmarksController#personal'
Completed 500 Internal Server Error in 61ms (ActiveRecord: 10.7ms (5 queries, 0 cached) | GC: 0.8ms)



ActiveRecord::StatementInvalid - Trilogy::ProtocolError: 1066: Not unique table/alias: 'resources' (trilogy_query_recv):
  app/controllers/bookmarks_controller.rb:31:in `personal'

@maebeale maebeale force-pushed the maebeale/fix-bookmark-duplicate-join branch from 84fa5a9 to 47448e5 Compare March 4, 2026 00:19
@maebeale
Copy link
Collaborator Author

maebeale commented Mar 4, 2026

@jmilljr24 would you try again?

@maebeale maebeale force-pushed the maebeale/fix-bookmark-duplicate-join branch from 47448e5 to 2e297d8 Compare March 4, 2026 11:50
@maebeale maebeale changed the title Fix duplicate table join crash on bookmarks page HOLD: Fix duplicate table join crash on bookmarks page Mar 4, 2026
@maebeale maebeale force-pushed the maebeale/fix-bookmark-duplicate-join branch 2 times, most recently from 421e33a to ad92c41 Compare March 4, 2026 12:56
@maebeale maebeale changed the title HOLD: Fix duplicate table join crash on bookmarks page Fix duplicate .sorted() call causing double joins on bookmarks page Mar 4, 2026
Bookmark.search already calls .sorted() internally, but both
BookmarksController#index and #personal were calling .sorted() again
on the result. This doubled the SQL joins, causing a ProtocolError
in production when sorting by title.

Also fixes existing specs that replicated the same double-sort pattern,
and adds test coverage for title filter + title sort combinations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@maebeale maebeale force-pushed the maebeale/fix-bookmark-duplicate-join branch from ad92c41 to a303bb7 Compare March 4, 2026 14:31
@maebeale maebeale requested a review from jmilljr24 March 4, 2026 14:39
@jmilljr24 jmilljr24 merged commit 300c51f into main Mar 4, 2026
3 checks passed
@jmilljr24 jmilljr24 deleted the maebeale/fix-bookmark-duplicate-join branch March 4, 2026 18:18
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.

2 participants