Remember recent searches

This commit is contained in:
Jorge Manrubia
2025-06-24 10:41:06 +02:00
parent 67fa9a46a8
commit 4f01b1763b
11 changed files with 158 additions and 42 deletions
+6 -2
View File
@@ -1,8 +1,12 @@
class SearchesController < ApplicationController
MAX_RESULTS = 50
def create
Current.user.remember_search(query_param)
head :ok
end
def show
@search = Search.new(Current.user, query_param)
@search_results = Current.user.search(query_param, max_results: 50)
@recent_search_queries = Current.user.search_queries.order(created_at: :desc).limit(10)
end
private
+1 -1
View File
@@ -7,7 +7,7 @@ class Search
def initialize(user, query, max_results: 50)
@user = user
@query = Query.new(query)
@query = Query.wrap(query)
@max_results = max_results
end
+12 -18
View File
@@ -1,32 +1,26 @@
class Search::Query
attr_reader :terms
class Search::Query < ApplicationRecord
validates :terms, presence: true
before_validation :sanitize_query_syntax
class << self
def wrap(query)
if query.is_a?(self)
self.new(query)
else
query
else
self.new(terms: query)
end
end
end
def initialize(terms)
@terms = sanitize_query_syntax(terms)
end
def valid?
@terms.present?
end
alias to_s terms
alias_attribute :to_s, :terms
private
def sanitize_query_syntax(terms)
terms = terms.to_s
terms = remove_invalid_search_characters(terms)
terms = remove_unbalanced_quotes(terms)
terms.presence
def sanitize_query_syntax
self.terms = begin
terms = remove_invalid_search_characters(self.terms)
terms = remove_unbalanced_quotes(terms)
terms.presence
end
end
def remove_invalid_search_characters(terms)
+2 -1
View File
@@ -1,5 +1,6 @@
class User < ApplicationRecord
include Accessor, ActionText::Attachable, Assignee, Mentionable, Named, Role, SignalUser, Transferable
include Accessor, ActionText::Attachable, Assignee, Mentionable, Named, Role, Searcher,
SignalUser, Transferable
include Timelined # Depends on Accessor
has_one_attached :avatar
+15
View File
@@ -0,0 +1,15 @@
module User::Searcher
extend ActiveSupport::Concern
included do
has_many :search_queries, class_name: "Search::Query", dependent: :destroy
end
def search(terms, max_results: 50)
Search.new(self, terms, max_results: max_results).results
end
def remember_search(terms)
search_queries.create(terms: terms) if search_queries.last&.terms != terms
end
end
+11
View File
@@ -0,0 +1,11 @@
<%= form_with url: search_path, method: :get, class: "search__form" do |form| %>
<div class="search__field">
<%= text_field_tag :q, params[:q], class: "input", type: "search", placeholder: "What are you looking for?" %>
<span class="search__field-icon btn btn--circle borderless">
<%= icon_tag "search" %>
</span>
<%= link_to search_path, class: "search__field-clear btn btn--circle borderless" do %>
<%= icon_tag "close" %>
<% end %>
</div>
<% end %>
@@ -0,0 +1,3 @@
<li>
<%= link_to search_query.terms, search_path(q: search_query.terms) %>
</li>
+5 -17
View File
@@ -13,27 +13,15 @@
<% end %>
<section class="search">
<%= form_with url: search_path, method: :get, class: "search__form" do |form| %>
<div class="search__field">
<%= text_field_tag :q, params[:q], class: "input", type: "search", placeholder: "What are you looking for?" %>
<span class="search__field-icon btn btn--circle borderless">
<%= icon_tag "search" %>
</span>
<%= link_to search_path, class: "search__field-clear btn btn--circle borderless" do %>
<%= icon_tag "close" %>
<% end %>
</div>
<% end %>
<%= render "searches/form" %>
<%= auto_submit_form_with url: search_path(q: params[:q]), method: :post %>
<ul class="search__list">
<%= render partial: "searches/result", collection: @search.results %>
<%= render partial: "searches/result", collection: @search_results %>
</ul>
<ul class="search__history">
<% 7.times do %>
<li>
<%= link_to "historic search term", "#" %>
</li>
<% end %>
<%= render partial: "searches/search_query", collection: @recent_search_queries %>
</ul>
</section>
@@ -0,0 +1,12 @@
class CreateSearchQueries < ActiveRecord::Migration[8.1]
def change
create_table :search_queries do |t|
t.references :user, null: false, foreign_key: true
t.string :terms, limit: 2000, null: false
t.timestamps
t.index %i[ user_id terms ]
end
end
end
Generated
+12 -1
View File
@@ -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_06_24_063244) do
ActiveRecord::Schema[8.1].define(version: 2025_06_24_080408) do
create_table "accesses", force: :cascade do |t|
t.integer "collection_id", null: false
t.datetime "created_at", null: false
@@ -304,6 +304,15 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_24_063244) do
# Unknown type '' for column 'rowid'
create_table "search_queries", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "terms", limit: 2000, null: false
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"], name: "index_search_queries_on_user_id"
end
create_table "search_results", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@@ -331,6 +340,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_24_063244) do
t.datetime "created_at", null: false
t.string "title"
t.datetime "updated_at", null: false
t.index ["title"], name: "index_tags_on_account_id_and_title", unique: true
end
create_table "users", force: :cascade do |t|
@@ -391,6 +401,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_24_063244) do
add_foreign_key "notifications", "users", column: "creator_id"
add_foreign_key "pins", "cards"
add_foreign_key "pins", "users"
add_foreign_key "search_queries", "users"
add_foreign_key "sessions", "users"
add_foreign_key "taggings", "cards"
add_foreign_key "taggings", "tags"
+79 -2
View File
@@ -913,6 +913,31 @@ columns:
default_function:
collation:
comment:
search_queries:
- *5
- *6
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
name: terms
cast_type: !ruby/object:ActiveModel::Type::String
true: t
false: f
precision:
scale:
limit: 2000
sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
sql_type: varchar(2000)
type: :string
limit: 2000
precision:
scale:
'null': false
default:
default_function:
collation:
comment:
- *9
- *25
search_results:
- *5
- *6
@@ -1088,6 +1113,7 @@ primary_keys:
reactions: id
schema_migrations: version
search_embeddings_vector_chunks00: rowid
search_queries: id
search_results: id
sessions: id
taggings: id
@@ -1130,6 +1156,7 @@ data_sources:
reactions: true
schema_migrations: true
search_embeddings_vector_chunks00: true
search_queries: true
search_results: true
sessions: true
taggings: true
@@ -2191,6 +2218,40 @@ indexes:
valid: true
schema_migrations: []
search_embeddings_vector_chunks00: []
search_queries:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: search_queries
name: index_search_queries_on_user_id
unique: false
columns:
- user_id
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: search_queries
name: index_search_queries_on_user_id_and_terms
unique: false
columns:
- user_id
- terms
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
search_results: []
sessions:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
@@ -2243,7 +2304,23 @@ indexes:
nulls_not_distinct:
comment:
valid: true
tags: []
tags:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: tags
name: index_tags_on_account_id_and_title
unique: true
columns:
- title
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
users:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: users
@@ -2344,4 +2421,4 @@ indexes:
comment:
valid: true
workflows: []
version: 20250624063244
version: 20250624080408