mirror of
https://github.com/Neetpone/foalfetch.git
synced 2025-03-12 06:30:08 +01:00
fix: fix author links, refactor search scope junk
This commit is contained in:
parent
04a814b71b
commit
390b7ac486
8 changed files with 86 additions and 70 deletions
|
@ -56,4 +56,8 @@ Layout/CaseIndentation:
|
||||||
IndentOneStep: true
|
IndentOneStep: true
|
||||||
|
|
||||||
Style/NumericPredicate:
|
Style/NumericPredicate:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# I've always been an "unnecessary this->" guy myself.
|
||||||
|
Style/RedundantSelf:
|
||||||
Enabled: false
|
Enabled: false
|
10
Gemfile
10
Gemfile
|
@ -11,16 +11,16 @@ gem 'sprockets-rails'
|
||||||
gem 'pg', '~> 1.1'
|
gem 'pg', '~> 1.1'
|
||||||
gem 'redis'
|
gem 'redis'
|
||||||
|
|
||||||
|
# Views
|
||||||
|
gem 'kaminari' # Must be included before ElasticSearch
|
||||||
|
gem 'redcarpet'
|
||||||
|
gem 'slim-rails'
|
||||||
|
|
||||||
# Search
|
# Search
|
||||||
gem 'elasticsearch-model'
|
gem 'elasticsearch-model'
|
||||||
gem 'fancy_searchable', github: 'Twibooru/fancy_searchable', ref: '40687c9'
|
gem 'fancy_searchable', github: 'Twibooru/fancy_searchable', ref: '40687c9'
|
||||||
gem 'model-msearch'
|
gem 'model-msearch'
|
||||||
|
|
||||||
# Views
|
|
||||||
gem 'kaminari'
|
|
||||||
gem 'redcarpet'
|
|
||||||
gem 'slim-rails'
|
|
||||||
|
|
||||||
# Programs
|
# Programs
|
||||||
gem 'puma', '~> 5.0'
|
gem 'puma', '~> 5.0'
|
||||||
gem 'sidekiq'
|
gem 'sidekiq'
|
||||||
|
|
|
@ -82,7 +82,6 @@ GEM
|
||||||
rake (>= 10.4, < 14.0)
|
rake (>= 10.4, < 14.0)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
base64 (0.2.0)
|
base64 (0.2.0)
|
||||||
bcrypt (3.1.20)
|
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
bullet (7.1.6)
|
bullet (7.1.6)
|
||||||
|
@ -301,7 +300,6 @@ PLATFORMS
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
annotate
|
annotate
|
||||||
bcrypt
|
|
||||||
bullet
|
bullet
|
||||||
capybara
|
capybara
|
||||||
debug
|
debug
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class AuthorsController < ApplicationController
|
class AuthorsController < ApplicationController
|
||||||
def show
|
def show
|
||||||
@author = Author.find(params[:id])
|
author = Author.select(:name).find(params[:id])
|
||||||
|
scope = SearchScope.new(nil, { author: author.name })
|
||||||
|
|
||||||
|
redirect_to "/search?scope=#{scope.scope_key}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,11 +11,20 @@ class SearchController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def search
|
def search
|
||||||
unless setup_scope
|
# unless params[:scope]
|
||||||
return
|
# return redirect_to root_path
|
||||||
|
# end
|
||||||
|
|
||||||
|
@scope = SearchScope.new(params)
|
||||||
|
|
||||||
|
# The scope is valid if was successfully used to load the existing search params
|
||||||
|
unless @scope.scope_loaded
|
||||||
|
return redirect_to "/search?scope=#{@scope.scope_key}"
|
||||||
end
|
end
|
||||||
|
|
||||||
using_random = @search_params['luck'].present?
|
@search_params = @scope.search_params
|
||||||
|
|
||||||
|
using_random = @search_params[:luck].present?
|
||||||
|
|
||||||
# This was mainly written this way to match the way FiMFetch's query interface looks, without using JS.
|
# This was mainly written this way to match the way FiMFetch's query interface looks, without using JS.
|
||||||
# I should do a Derpibooru-esque textual search system sometime.
|
# I should do a Derpibooru-esque textual search system sometime.
|
||||||
|
@ -23,8 +32,8 @@ class SearchController < ApplicationController
|
||||||
per_page: @per_page,
|
per_page: @per_page,
|
||||||
page: @page_num
|
page: @page_num
|
||||||
) do |s|
|
) do |s|
|
||||||
s.add_query(match: { title: { query: @search_params['q'], operator: :and } }) if @search_params['q'].present?
|
s.add_query(match: { title: { query: @search_params[:q], operator: :and } }) if @search_params[:q].present?
|
||||||
s.add_query(match: { author: { query: @search_params['author'], operator: :and } }) if @search_params['author'].present?
|
s.add_query(match: { author: { query: @search_params[:author], operator: :and } }) if @search_params[:author].present?
|
||||||
|
|
||||||
# tags -> match any of the included tags, exclude any of the excluded tags
|
# tags -> match any of the included tags, exclude any of the excluded tags
|
||||||
tag_musts, tag_must_nots = parse_tag_queries
|
tag_musts, tag_must_nots = parse_tag_queries
|
||||||
|
@ -42,13 +51,13 @@ class SearchController < ApplicationController
|
||||||
|
|
||||||
# ratings -> match stories with any of them
|
# ratings -> match stories with any of them
|
||||||
s.add_filter(bool: {
|
s.add_filter(bool: {
|
||||||
should: @search_params['ratings'].keys.map { |k| { term: { content_rating: k } } }
|
should: @search_params[:ratings].keys.map { |k| { term: { content_rating: k } } }
|
||||||
}) if @search_params['ratings'].present?
|
}) if @search_params[:ratings].present?
|
||||||
|
|
||||||
# completeness -> match stories with any of them
|
# completeness -> match stories with any of them
|
||||||
s.add_filter(bool: {
|
s.add_filter(bool: {
|
||||||
should: @search_params['state'].keys.map { |k| { term: { completion_status: k } } }
|
should: @search_params[:state].keys.map { |k| { term: { completion_status: k } } }
|
||||||
}) if @search_params['state'].present?
|
}) if @search_params[:state].present?
|
||||||
|
|
||||||
# sort direction
|
# sort direction
|
||||||
if using_random
|
if using_random
|
||||||
|
@ -75,7 +84,7 @@ class SearchController < ApplicationController
|
||||||
|
|
||||||
# returns: [included tags, excluded tags]
|
# returns: [included tags, excluded tags]
|
||||||
def parse_tag_queries
|
def parse_tag_queries
|
||||||
tag_searches = "#{@search_params['tags']},#{@search_params['characters']}".split(',').reject(&:blank?)
|
tag_searches = "#{@search_params[:tas]},#{@search_params[:characters]}".split(',').reject(&:blank?)
|
||||||
|
|
||||||
[
|
[
|
||||||
tag_searches.reject { |t| t[0] == '-' },
|
tag_searches.reject { |t| t[0] == '-' },
|
||||||
|
@ -85,8 +94,8 @@ class SearchController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_sort
|
def parse_sort
|
||||||
sf = ALLOWED_SORT_FIELDS.detect { |f| @search_params['sf'] == f.to_s } || :date_updated
|
sf = ALLOWED_SORT_FIELDS.detect { |f| @search_params[:sf] == f.to_s } || :date_updated
|
||||||
sd = ALLOWED_SORT_DIRS.detect { |d| @search_params['sd'] == d.to_s } || :desc
|
sd = ALLOWED_SORT_DIRS.detect { |d| @search_params[:sd] == d.to_s } || :desc
|
||||||
|
|
||||||
# we need to sort on the keyword versions of text fields, to avoid using fielddata.
|
# we need to sort on the keyword versions of text fields, to avoid using fielddata.
|
||||||
sf = case sf
|
sf = case sf
|
||||||
|
@ -102,40 +111,4 @@ class SearchController < ApplicationController
|
||||||
|
|
||||||
{sf => sd}
|
{sf => sd}
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: This is some of the worst Ruby code I have ever written.
|
|
||||||
def setup_scope
|
|
||||||
@scope_key = Random.hex(16)
|
|
||||||
scope_valid = false
|
|
||||||
|
|
||||||
# scope passed, try to look it up in redis and use the search params from it
|
|
||||||
if params[:scope].present?
|
|
||||||
result = $redis.get("search_scope/#{params[:scope]}")
|
|
||||||
if result.present?
|
|
||||||
@search_params = JSON.parse(result)
|
|
||||||
@scope_key = params[:scope]
|
|
||||||
scope_valid = true
|
|
||||||
else
|
|
||||||
redirect_to '/'
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
@search_params = params
|
|
||||||
end
|
|
||||||
|
|
||||||
# you can't JSON.dump a Parameters
|
|
||||||
if @search_params.is_a? ActionController::Parameters
|
|
||||||
@search_params = @search_params.permit!.to_h
|
|
||||||
end
|
|
||||||
$redis.setex("search_scope/#{@scope_key}", 3600, JSON.dump(@search_params))
|
|
||||||
|
|
||||||
# if the scope was invalid (no key passed, or invalid key passed), redirect to the results page
|
|
||||||
# with the new key we just generated for the params we had.
|
|
||||||
unless scope_valid
|
|
||||||
redirect_to "/search?scope=#{@scope_key}"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
38
app/lib/search_scope.rb
Normal file
38
app/lib/search_scope.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class SearchScope
|
||||||
|
attr_reader :search_params, :scope_key, :scope_loaded
|
||||||
|
|
||||||
|
def initialize(params, search_params = nil)
|
||||||
|
@scope_key = SecureRandom.hex 16
|
||||||
|
@scope_loaded = false
|
||||||
|
|
||||||
|
if search_params
|
||||||
|
@search_params = search_params
|
||||||
|
self.persist
|
||||||
|
elsif params
|
||||||
|
self.load_from_params(params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_from_params(params)
|
||||||
|
if params[:scope].present?
|
||||||
|
result = $redis.get("search_scope/#{params[:scope]}")
|
||||||
|
if result.present?
|
||||||
|
@search_params = JSON.parse(result, symbolize_names: true)
|
||||||
|
@scope_key = params[:scope]
|
||||||
|
@scope_loaded = true
|
||||||
|
self.persist # refresh the expiry
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@search_params = params.permit!.to_h.symbolize_keys
|
||||||
|
self.persist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def persist
|
||||||
|
# Nice long expiry so nobody's search disappears if they don't touch it for awhile.
|
||||||
|
$redis.setex("search_scope/#{@scope_key}", 1.day.in_seconds, JSON.dump(@search_params))
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,28 +9,28 @@ div.search-adv
|
||||||
b Rating
|
b Rating
|
||||||
br
|
br
|
||||||
= label_tag "ratings_everyone"
|
= label_tag "ratings_everyone"
|
||||||
= check_box_tag "ratings[everyone]", 1, @search_params.dig('ratings', 'everyone').present?
|
= check_box_tag "ratings[everyone]", 1, @search_params.dig(:ratings, 'everyone').present?
|
||||||
| Everyone
|
| Everyone
|
||||||
= label_tag "ratings_teen"
|
= label_tag "ratings_teen"
|
||||||
= check_box_tag "ratings[teen]", 1, @search_params.dig('ratings', 'teen').present?
|
= check_box_tag "ratings[teen]", 1, @search_params.dig(:ratings, 'teen').present?
|
||||||
| Teen
|
| Teen
|
||||||
= label_tag "ratings_mature"
|
= label_tag "ratings_mature"
|
||||||
= check_box_tag "ratings[mature]", 1, @search_params.dig('ratings', 'mature').present?
|
= check_box_tag "ratings[mature]", 1, @search_params.dig(:ratings, 'mature').present?
|
||||||
| Mature
|
| Mature
|
||||||
.opts
|
.opts
|
||||||
b Story State
|
b Story State
|
||||||
br
|
br
|
||||||
= label_tag "state_complete"
|
= label_tag "state_complete"
|
||||||
= check_box_tag "state[complete]", 1, @search_params.dig('state', 'complete').present?
|
= check_box_tag "state[complete]", 1, @search_params.dig(:state, 'complete').present?
|
||||||
| Complete
|
| Complete
|
||||||
= label_tag "state_incomplete"
|
= label_tag "state_incomplete"
|
||||||
= check_box_tag "state[incomplete]", 1, @search_params.dig('state', 'incomplete').present?
|
= check_box_tag "state[incomplete]", 1, @search_params.dig(:state, 'incomplete').present?
|
||||||
| Incomplete
|
| Incomplete
|
||||||
= label_tag "state_hiatus"
|
= label_tag "state_hiatus"
|
||||||
= check_box_tag "state[hiatus]", 1, @search_params.dig('state', 'hiatus').present?
|
= check_box_tag "state[hiatus]", 1, @search_params.dig(:state, 'hiatus').present?
|
||||||
| On Hiatus
|
| On Hiatus
|
||||||
= label_tag "state_cancelled"
|
= label_tag "state_cancelled"
|
||||||
= check_box_tag "state[cancelled]", 1, @search_params.dig('state', 'cancelled').present?
|
= check_box_tag "state[cancelled]", 1, @search_params.dig(:state, 'cancelled').present?
|
||||||
| Cancelled
|
| Cancelled
|
||||||
/.opts
|
/.opts
|
||||||
b Story Age
|
b Story Age
|
||||||
|
@ -73,7 +73,7 @@ div.search-adv
|
||||||
.opts
|
.opts
|
||||||
b Author:
|
b Author:
|
||||||
br
|
br
|
||||||
= text_field_tag :author, @search_params['author']
|
= text_field_tag :author, @search_params[:author]
|
||||||
/.cols
|
/.cols
|
||||||
.opts
|
.opts
|
||||||
b FiMFiction Rating:
|
b FiMFiction Rating:
|
||||||
|
@ -104,7 +104,7 @@ div.search-adv
|
||||||
br
|
br
|
||||||
.js-tag-editor
|
.js-tag-editor
|
||||||
.selected-tags
|
.selected-tags
|
||||||
= text_field_tag :characters, @search_params['characters']
|
= text_field_tag :characters, @search_params[:characters]
|
||||||
= select_tag :fancy_tags, options_for_select(@character_tags)
|
= select_tag :fancy_tags, options_for_select(@character_tags)
|
||||||
.cols
|
.cols
|
||||||
.opts
|
.opts
|
||||||
|
@ -112,15 +112,15 @@ div.search-adv
|
||||||
br
|
br
|
||||||
.js-tag-editor
|
.js-tag-editor
|
||||||
.selected-tags
|
.selected-tags
|
||||||
= text_field_tag :tags, @search_params['tags']
|
= text_field_tag :tags, @search_params[:tags]
|
||||||
= select_tag :fancy_characters, options_for_select(@other_tags)
|
= select_tag :fancy_characters, options_for_select(@other_tags)
|
||||||
.cols
|
.cols
|
||||||
.opts
|
.opts
|
||||||
b Sort By
|
b Sort By
|
||||||
br
|
br
|
||||||
=> select_tag :sf, options_for_select({'Query Relevance' => 'rel', 'Title' => 'title', 'Author' => 'author', \
|
=> select_tag :sf, options_for_select({'Query Relevance' => 'rel', 'Title' => 'title', 'Author' => 'author', \
|
||||||
'Publish Date' => 'date_published', 'Updated Date' => 'date_updated', 'Word Count' => 'num_words'}, @search_params['sf'])
|
'Publish Date' => 'date_published', 'Updated Date' => 'date_updated', 'Word Count' => 'num_words'}, @search_params[:sf])
|
||||||
= select_tag :sd, options_for_select({'High to Low' => 'desc', 'Low to High' => 'asc'}, @search_params['sd'])
|
= select_tag :sd, options_for_select({'High to Low' => 'desc', 'Low to High' => 'asc'}, @search_params[:sd])
|
||||||
- if show_button
|
- if show_button
|
||||||
.buttons
|
.buttons
|
||||||
= submit_tag 'Go Fetch!', name: 'search'
|
= submit_tag 'Go Fetch!', name: 'search'
|
|
@ -5,7 +5,7 @@
|
||||||
a.logo href="/"
|
a.logo href="/"
|
||||||
= image_tag '/img/logo_small.png'
|
= image_tag '/img/logo_small.png'
|
||||||
.searchbox
|
.searchbox
|
||||||
= search_field_tag 'q', @search_params['q'], placeholder: 'Search story titles...'
|
= search_field_tag :q, @search_params[:q], placeholder: 'Search story titles...'
|
||||||
= render partial: 'advanced', locals: { show_button: true }
|
= render partial: 'advanced', locals: { show_button: true }
|
||||||
|
|
||||||
- @records.each do |rec|
|
- @records.each do |rec|
|
||||||
|
@ -57,6 +57,6 @@
|
||||||
' Last Update
|
' Last Update
|
||||||
span= rec.date_updated
|
span= rec.date_updated
|
||||||
.searchnav
|
.searchnav
|
||||||
= paginate(@records, params: { scope: @scope_key })
|
= paginate(@records, params: { scope: @scope.scope_key })
|
||||||
br
|
br
|
||||||
span.searchtime Found #{@search.total_count} results.
|
span.searchtime Found #{@search.total_count} results.
|
Loading…
Add table
Reference in a new issue