diff --git a/app/controllers/searches_controller.rb b/app/controllers/searches_controller.rb index c788f1b52..1ccbaa245 100644 --- a/app/controllers/searches_controller.rb +++ b/app/controllers/searches_controller.rb @@ -3,6 +3,6 @@ class SearchesController < ApplicationController def show @search_results = Current.user.search(@query_terms).limit(50) - @recent_search_queries = Current.user.search_queries.order(created_at: :desc).limit(50).uniq(&:terms) + @recent_search_queries = Current.user.search_queries.order(updated_at: :desc).limit(10) end end diff --git a/app/models/user/searcher.rb b/app/models/user/searcher.rb index a47d9cc96..345676e4f 100644 --- a/app/models/user/searcher.rb +++ b/app/models/user/searcher.rb @@ -10,6 +10,8 @@ module User::Searcher end def remember_search(terms) - search_queries.create(terms: terms) if search_queries.last&.terms != terms + search_queries.find_or_create_by(terms: terms).tap do |search_query| + search_query.touch unless search_query.previously_new_record? + end end end diff --git a/db/migrate/20250703193928_add_index_to_search_queries.rb b/db/migrate/20250703193928_add_index_to_search_queries.rb new file mode 100644 index 000000000..d8cd7bc59 --- /dev/null +++ b/db/migrate/20250703193928_add_index_to_search_queries.rb @@ -0,0 +1,5 @@ +class AddIndexToSearchQueries < ActiveRecord::Migration[8.1] + def change + add_index :search_queries, %w[ user_id updated_at ], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index ea2c34fc1..7f5461614 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2025_07_02_211937) do +ActiveRecord::Schema[8.1].define(version: 2025_07_03_193928) do create_table "accesses", force: :cascade do |t| t.datetime "accessed_at" t.integer "collection_id", null: false @@ -315,6 +315,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_07_02_211937) do t.datetime "updated_at", null: false t.integer "user_id", null: false t.index ["user_id", "terms"], name: "index_search_queries_on_user_id_and_terms" + t.index ["user_id", "updated_at"], name: "index_search_queries_on_user_id_and_updated_at", unique: true t.index ["user_id"], name: "index_search_queries_on_user_id" end diff --git a/db/schema_cache.yml b/db/schema_cache.yml index b4653b357..1f58a58a8 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -2312,6 +2312,23 @@ indexes: nulls_not_distinct: comment: valid: true + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: search_queries + name: index_search_queries_on_user_id_and_updated_at + unique: true + columns: + - user_id + - updated_at + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true search_results: [] sessions: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition @@ -2499,4 +2516,4 @@ indexes: comment: valid: true workflows: [] -version: 20250702211937 +version: 20250703193928 diff --git a/script/remove_duplicated_search_queries.rb b/script/remove_duplicated_search_queries.rb new file mode 100755 index 000000000..b810d5286 --- /dev/null +++ b/script/remove_duplicated_search_queries.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +require_relative "../config/environment" + +ApplicationRecord.with_each_tenant do |tenant| + User.find_each do |user| + search_queries = Set.new + to_delete = [] + user.search_queries.find_each do |search_query| + if search_queries.include?(search_query.terms) + to_delete << search_query + end + + search_queries << search_query.terms + end + + to_delete.each(&:destroy) + end +end diff --git a/test/models/user/searcher_test.rb b/test/models/user/searcher_test.rb index 70e2a4914..35a95cd48 100644 --- a/test/models/user/searcher_test.rb +++ b/test/models/user/searcher_test.rb @@ -13,11 +13,16 @@ class User::SearcherTest < ActiveSupport::TestCase assert_equal "broken", @user.search_queries.last.terms end - test "don't duplicate repeated searches" do - @user.remember_search("broken") + test "don't duplicate repeated searches but touch the existing match" do + search_result = @user.remember_search("broken") + original_updated_at = search_result.updated_at + + travel_to 1.day.from_now assert_no_difference -> { @user.search_queries.count }, +1 do @user.remember_search("broken") end + + assert search_result.reload.updated_at > original_updated_at end end