mirror of
https://github.com/Neetpone/foalfetch.git
synced 2025-02-08 18:06:43 +01:00
126 lines
3.8 KiB
Ruby
126 lines
3.8 KiB
Ruby
|
# This whole class is a giant mess but I coded it fast so give me a break.
|
||
|
class SearchController < ApplicationController
|
||
|
ALLOWED_SORT_DIRS = [:asc, :desc]
|
||
|
ALLOWED_SORT_FIELDS = [:title, :author, :date_published, :date_updated, :num_words, :rel]
|
||
|
|
||
|
before_action :load_tags
|
||
|
|
||
|
def index
|
||
|
@search_params = {}
|
||
|
end
|
||
|
|
||
|
def search
|
||
|
unless setup_scope
|
||
|
return
|
||
|
end
|
||
|
|
||
|
using_random = @search_params['luck'].present?
|
||
|
|
||
|
# 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.
|
||
|
@search = Story.fancy_search(per_page: @per_page,
|
||
|
page: @page_num) do |s|
|
||
|
s.add_query match: { title: { query: @search_params['q'], operator: 'AND' } } unless @search_params['q'].blank?
|
||
|
s.add_query match: { author: { query: @search_params['author'], operator: 'AND' } } if @search_params['author'].present?
|
||
|
|
||
|
# ratings -> match stories with any of them
|
||
|
s.add_filter bool: {
|
||
|
should: @search_params['ratings'].keys.map { |k| { term: { content_rating: k } } }
|
||
|
} unless @search_params['ratings'].blank?
|
||
|
|
||
|
# completeness -> match stories with any of them
|
||
|
s.add_filter bool: {
|
||
|
should: @search_params['state'].keys.map { |k| { term: { completion_status: k } } }
|
||
|
} unless @search_params['state'].blank?
|
||
|
|
||
|
# tags -> match any of the included tags, exclude any of the excluded taags
|
||
|
tag_musts, tag_must_nots = parse_tag_queries
|
||
|
|
||
|
s.add_filter terms: {
|
||
|
tags: tag_musts
|
||
|
} if tag_musts.any?
|
||
|
|
||
|
s.add_filter bool: {
|
||
|
must_not: tag_must_nots.map { |t| { term: { tags: t } } }
|
||
|
} if tag_must_nots.any?
|
||
|
|
||
|
# sort direction
|
||
|
if using_random
|
||
|
s.add_sort _random: :desc
|
||
|
else
|
||
|
s.add_sort parse_sort
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if using_random && @search.total_count > 0
|
||
|
redirect_to story_path(@search.records[0])
|
||
|
return
|
||
|
end
|
||
|
|
||
|
@records = @search.records
|
||
|
end
|
||
|
|
||
|
private
|
||
|
def load_tags
|
||
|
@character_tags = Tag.where(type: 'character').pluck(:name)
|
||
|
@other_tags = Tag.where.not(type: 'character').pluck(:name)
|
||
|
end
|
||
|
|
||
|
# returns: [included tags, excluded tags]
|
||
|
def parse_tag_queries
|
||
|
tag_searches = (@search_params['tags'] + ',' + @search_params['characters']).split(',').reject &:blank?
|
||
|
|
||
|
[tag_searches.select { |t| t[0] != '-' }, tag_searches.select { |t| t[0] == '-' }]
|
||
|
end
|
||
|
|
||
|
def parse_sort
|
||
|
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
|
||
|
|
||
|
sf = case sf
|
||
|
when :rel then
|
||
|
:_score
|
||
|
else
|
||
|
sf
|
||
|
end
|
||
|
|
||
|
{sf => sd}
|
||
|
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.load(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
|