Implements advanced filters (#90) (#91)

* Add advanced filters

Adds new filters to search:

* Full auto
* Charge attack
* Auto guard
* Number of weapons (user-selectable now)
* Number of summons
* Number of characters
* Maximum number of turns
* Maximum number of buttons
* Maximum clear time
* User quality (No anonymous users)
* Name quality (No untitled teams)
* Remixes (Only show original teams)

* Update advanced filter params

* Add default to party counter cache
This commit is contained in:
Justin Edmund 2023-04-09 19:40:41 -07:00 committed by GitHub
parent 6fd5aa8c27
commit 6e81ffc7dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 241 additions and 22 deletions

View file

@ -8,6 +8,16 @@ module Api
before_action :set, only: %w[update destroy]
before_action :authorize, only: %w[update destroy]
MAX_CHARACTERS = 5
MAX_SUMMONS = 8
MAX_WEAPONS = 13
DEFAULT_MIN_CHARACTERS = 3
DEFAULT_MIN_SUMMONS = 2
DEFAULT_MIN_WEAPONS = 5
DEFAULT_MAX_CLEAR_TIME = 5400
def create
party = Party.new
party.user = current_user if current_user
@ -73,12 +83,15 @@ module Api
end
def index
conditions = build_conditions(request.params)
conditions = build_conditions
@parties = Party.joins(:weapons)
.group('parties.id')
.having('count(distinct grid_weapons.weapon_id) > 2')
.where(conditions)
.where(name_quality)
.where(user_quality)
.where(original)
.order(created_at: :desc)
.paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE)
.each { |party| party.favorited = current_user ? party.is_favorited(current_user) : false }
@ -99,11 +112,14 @@ module Api
def favorites
raise Api::V1::UnauthorizedError unless current_user
conditions = build_conditions(request.params)
conditions = build_conditions
conditions[:favorites] = { user_id: current_user.id }
@parties = Party.joins(:favorites)
.where(conditions)
.where(name_quality)
.where(user_quality)
.where(original)
.order('favorites.created_at DESC')
.paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE)
.each { |party| party.favorited = party.is_favorited(current_user) }
@ -127,20 +143,72 @@ module Api
render_unauthorized_response if @party.user != current_user || @party.edit_key != edit_key
end
def build_conditions(params)
def build_conditions
params = request.params
unless params['recency'].blank?
start_time = (DateTime.current - params['recency'].to_i.seconds)
.to_datetime.beginning_of_day
end
min_characters_count = params['characters_count'].blank? ? DEFAULT_MIN_CHARACTERS : params['characters_count'].to_i
min_summons_count = params['summons_count'].blank? ? DEFAULT_MIN_SUMMONS : params['summons_count'].to_i
min_weapons_count = params['weapons_count'].blank? ? DEFAULT_MIN_WEAPONS : params['weapons_count'].to_i
max_clear_time = params['max_clear_time'].blank? ? DEFAULT_MAX_CLEAR_TIME : params['max_clear_time'].to_i
{}.tap do |hash|
hash[:element] = params['element'] unless params['element'].blank?
# Basic filters
hash[:element] = params['element'].to_i unless params['element'].blank?
hash[:raid] = params['raid'] unless params['raid'].blank?
hash[:created_at] = start_time..DateTime.current unless params['recency'].blank?
hash[:weapons_count] = 5..13
# Advanced filters: Team parameters
hash[:full_auto] = params['full_auto'].to_i unless params['full_auto'].blank? || params['full_auto'].to_i == -1
hash[:auto_guard] = params['auto_guard'].to_i unless params['auto_guard'].blank? || params['auto_guard'].to_i == -1
hash[:charge_attack] = params['charge_attack'].to_i unless params['charge_attack'].blank? || params['charge_attack'].to_i == -1
# Turn count of 0 will not be displayed, so disallow on the frontend or set default to 1
# How do we do the same for button count since that can reasonably be 1?
# hash[:turn_count] = params['turn_count'].to_i unless params['turn_count'].blank? || params['turn_count'].to_i <= 0
# hash[:button_count] = params['button_count'].to_i unless params['button_count'].blank?
# hash[:clear_time] = 0..max_clear_time
# Advanced filters: Object counts
hash[:characters_count] = min_characters_count..MAX_CHARACTERS
hash[:summons_count] = min_summons_count..MAX_SUMMONS
hash[:weapons_count] = min_weapons_count..MAX_WEAPONS
end
end
def original
"source_party_id IS NULL" unless request.params['original'].blank? || request.params['original'] == "false"
end
def user_quality
"user_id IS NOT NULL" unless request.params[:user_quality].blank? || request.params[:user_quality] == "false"
end
def name_quality
low_quality = [
"Untitled",
"Remix of Untitled",
"Remix of Remix of Untitled",
"Remix of Remix of Remix of Untitled",
"Remix of Remix of Remix of Remix of Untitled",
"Remix of Remix of Remix of Remix of Remix of Untitled",
"無題",
"無題のリミックス",
"無題のリミックスのリミックス",
"無題のリミックスのリミックスのリミックス",
"無題のリミックスのリミックスのリミックスのリミックス",
"無題のリミックスのリミックスのリミックスのリミックスのリミックス"
]
joined_names = low_quality.map { |name| "'#{name}'" }.join(',')
"name NOT IN (#{joined_names})" unless request.params[:name_quality].blank? || request.params[:name_quality] == "false"
end
def remixed_name(name)
blanked_name = {
en: name.blank? ? 'Untitled team' : name,

View file

@ -8,6 +8,16 @@ module Api
before_action :set, except: %w[create check_email check_username]
before_action :set_by_id, only: %w[info update]
MAX_CHARACTERS = 5
MAX_SUMMONS = 8
MAX_WEAPONS = 13
DEFAULT_MIN_CHARACTERS = 3
DEFAULT_MIN_SUMMONS = 2
DEFAULT_MIN_WEAPONS = 5
DEFAULT_MAX_CLEAR_TIME = 5400
def create
user = User.new(user_params)
@ -44,11 +54,23 @@ module Api
render_not_found_response('user')
else
conditions = build_conditions(request.params)
conditions = build_conditions
conditions[:user_id] = @user.id
@parties = Party
.where(conditions)
.where(name_quality)
.where(user_quality)
.where(original)
.order(created_at: :desc)
.paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE)
.each { |party| party.favorited = party.is_favorited(current_user) }
parties = Party
.where(conditions)
.where(name_quality)
.where(user_quality)
.where(original)
.order(created_at: :desc)
.paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE)
.each do |party|
@ -81,19 +103,72 @@ module Api
private
def build_conditions(params)
def build_conditions
params = request.params
unless params['recency'].blank?
start_time = (DateTime.current - params['recency'].to_i.seconds)
.to_datetime.beginning_of_day
end
min_characters_count = params['characters_count'].blank? ? DEFAULT_MIN_CHARACTERS : params['characters_count'].to_i
min_summons_count = params['summons_count'].blank? ? DEFAULT_MIN_SUMMONS : params['summons_count'].to_i
min_weapons_count = params['weapons_count'].blank? ? DEFAULT_MIN_WEAPONS : params['weapons_count'].to_i
max_clear_time = params['max_clear_time'].blank? ? DEFAULT_MAX_CLEAR_TIME : params['max_clear_time'].to_i
{}.tap do |hash|
hash[:element] = params['element'] unless params['element'].blank?
# Basic filters
hash[:element] = params['element'].to_i unless params['element'].blank?
hash[:raid] = params['raid'] unless params['raid'].blank?
hash[:created_at] = start_time..DateTime.current unless params['recency'].blank?
# Advanced filters: Team parameters
hash[:full_auto] = params['full_auto'].to_i unless params['full_auto'].blank? || params['full_auto'].to_i == -1
hash[:auto_guard] = params['auto_guard'].to_i unless params['auto_guard'].blank? || params['auto_guard'].to_i == -1
hash[:charge_attack] = params['charge_attack'].to_i unless params['charge_attack'].blank? || params['charge_attack'].to_i == -1
# Turn count of 0 will not be displayed, so disallow on the frontend or set default to 1
# How do we do the same for button count since that can reasonably be 1?
# hash[:turn_count] = params['turn_count'].to_i unless params['turn_count'].blank? || params['turn_count'].to_i <= 0
# hash[:button_count] = params['button_count'].to_i unless params['button_count'].blank?
# hash[:clear_time] = 0..max_clear_time
# Advanced filters: Object counts
hash[:characters_count] = min_characters_count..MAX_CHARACTERS
hash[:summons_count] = min_summons_count..MAX_SUMMONS
hash[:weapons_count] = min_weapons_count..MAX_WEAPONS
end
end
def original
"source_party_id IS NULL" unless params['original'].blank? || params['original'] == '0'
end
def user_quality
"user_id IS NOT NULL" unless params[:user_quality].nil? || params[:user_quality] == "0"
end
def name_quality
low_quality = [
"Untitled",
"Remix of Untitled",
"Remix of Remix of Untitled",
"Remix of Remix of Remix of Untitled",
"Remix of Remix of Remix of Remix of Untitled",
"Remix of Remix of Remix of Remix of Remix of Untitled",
"無題",
"無題のリミックス",
"無題のリミックスのリミックス",
"無題のリミックスのリミックスのリミックス",
"無題のリミックスのリミックスのリミックスのリミックス",
"無題のリミックスのリミックスのリミックスのリミックスのリミックス"
]
joined_names = low_quality.map { |name| "'#{name}'" }.join(',')
"name NOT IN (#{joined_names})" unless params[:name_quality].nil? || params[:name_quality] == "0"
end
# Specify whitelisted properties that can be modified.
def set
@user = User.where('username = ?', params[:id]).first

View file

@ -0,0 +1,7 @@
class AddDefaultToCounterCacheOnParties < ActiveRecord::Migration[7.0]
def change
change_column_default :parties, :characters_count, 0
change_column_default :parties, :weapons_count, 0
change_column_default :parties, :summons_count, 0
end
end

View file

@ -10,19 +10,62 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
ActiveRecord::Schema[7.0].define(version: 2023_03_21_115930) do
# These are extensions that must be enabled in order to support this database
enable_extension "btree_gin"
enable_extension "pg_trgm"
enable_extension "pgcrypto"
enable_extension "plpgsql"
enable_extension "timescaledb"
create_table "app_updates", primary_key: "updated_at", id: :datetime, force: :cascade do |t|
t.string "update_type", null: false
t.string "version"
end
create_table "character_charge_attacks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "character_id"
t.string "name_en", null: false
t.string "name_jp", null: false
t.string "description_en", null: false
t.string "description_jp", null: false
t.integer "order", null: false
t.string "form"
t.uuid "effects", array: true
t.index ["character_id"], name: "index_character_charge_attacks_on_character_id"
end
create_table "character_skills", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "character_id"
t.string "name_en", null: false
t.string "name_jp", null: false
t.string "description_en", null: false
t.string "description_jp", null: false
t.integer "type", null: false
t.integer "position", null: false
t.string "form"
t.integer "cooldown", default: 0, null: false
t.integer "lockout", default: 0, null: false
t.integer "duration", array: true
t.boolean "recast", default: false, null: false
t.integer "obtained_at", default: 1, null: false
t.uuid "effects", array: true
t.index ["character_id"], name: "index_character_skills_on_character_id"
end
create_table "character_support_skills", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "character_id"
t.string "name_en", null: false
t.string "name_jp", null: false
t.string "description_en", null: false
t.string "description_jp", null: false
t.integer "position", null: false
t.integer "obtained_at"
t.boolean "emp", default: false, null: false
t.boolean "transcendence", default: false, null: false
t.uuid "effects", array: true
t.index ["character_id"], name: "index_character_support_skills_on_character_id"
end
create_table "characters", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "name_en"
t.string "name_jp"
@ -50,10 +93,26 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.integer "max_hp_ulb"
t.integer "max_atk_ulb"
t.integer "character_id", default: [], null: false, array: true
t.string "nicknames_en", default: [], null: false, array: true
t.string "nicknames_jp", default: [], null: false, array: true
t.index ["name_en"], name: "index_characters_on_name_en", opclass: :gin_trgm_ops, using: :gin
end
create_table "data_migrations", primary_key: "version", id: :string, force: :cascade do |t|
create_table "effects", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "name_en", null: false
t.string "name_jp", null: false
t.string "description_en", null: false
t.string "description_jp", null: false
t.integer "accuracy_value"
t.string "accuracy_suffix"
t.string "accuracy_comparator"
t.jsonb "strength", array: true
t.integer "healing_cap"
t.boolean "duration_indefinite", default: false, null: false
t.integer "duration_value"
t.string "duration_unit"
t.string "notes_en"
t.string "notes_jp"
end
create_table "favorites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@ -74,12 +133,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.datetime "updated_at", null: false
t.boolean "perpetuity", default: false, null: false
t.integer "transcendence_step", default: 0, null: false
t.jsonb "ring1", default: { "modifier" => nil, "strength" => nil }, null: false
t.jsonb "ring2", default: { "modifier" => nil, "strength" => nil }, null: false
t.jsonb "ring3", default: { "modifier" => nil, "strength" => nil }, null: false
t.jsonb "ring4", default: { "modifier" => nil, "strength" => nil }, null: false
t.jsonb "earring", default: { "modifier" => nil, "strength" => nil }, null: false
t.jsonb "awakening", default: { "type" => 1, "level" => 1 }, null: false
t.jsonb "ring1", default: {"modifier"=>nil, "strength"=>nil}, null: false
t.jsonb "ring2", default: {"modifier"=>nil, "strength"=>nil}, null: false
t.jsonb "ring3", default: {"modifier"=>nil, "strength"=>nil}, null: false
t.jsonb "ring4", default: {"modifier"=>nil, "strength"=>nil}, null: false
t.jsonb "earring", default: {"modifier"=>nil, "strength"=>nil}, null: false
t.jsonb "awakening", default: {"type"=>1, "level"=>1}, null: false
t.boolean "skill0_enabled", default: true, null: false
t.boolean "skill1_enabled", default: true, null: false
t.boolean "skill2_enabled", default: true, null: false
t.boolean "skill3_enabled", default: true, null: false
t.index ["character_id"], name: "index_grid_characters_on_character_id"
t.index ["party_id"], name: "index_grid_characters_on_party_id"
end
@ -94,6 +157,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "transcendence_step", default: 0, null: false
t.boolean "quick_summon", default: false, null: false
t.index ["party_id"], name: "index_grid_summons_on_party_id"
t.index ["summon_id"], name: "index_grid_summons_on_summon_id"
end
@ -155,12 +219,12 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.integer "proficiency2"
t.string "row"
t.boolean "master_level", default: false, null: false
t.boolean "ultimate_mastery", default: false, null: false
t.integer "order"
t.uuid "base_job_id"
t.string "granblue_id"
t.boolean "accessory", default: false
t.integer "accessory_type", default: 0
t.boolean "ultimate_mastery", default: false, null: false
t.index ["base_job_id"], name: "index_jobs_on_base_job_id"
end
@ -212,10 +276,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.text "description"
t.uuid "raid_id"
t.integer "element"
t.integer "weapons_count"
t.integer "weapons_count", default: 0
t.uuid "job_id"
t.integer "master_level"
t.integer "ultimate_mastery"
t.uuid "skill1_id"
t.uuid "skill2_id"
t.uuid "skill3_id"
@ -229,10 +292,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.integer "turn_count"
t.uuid "source_party_id"
t.uuid "accessory_id"
t.integer "characters_count"
t.integer "summons_count"
t.integer "characters_count", default: 0
t.integer "summons_count", default: 0
t.string "edit_key"
t.uuid "local_id"
t.integer "ultimate_mastery"
t.index ["accessory_id"], name: "index_parties_on_accessory_id"
t.index ["job_id"], name: "index_parties_on_job_id"
t.index ["skill0_id"], name: "index_parties_on_skill0_id"
@ -275,6 +339,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.boolean "xlb", default: false, null: false
t.integer "max_atk_xlb"
t.integer "max_hp_xlb"
t.string "nicknames_en", default: [], null: false, array: true
t.string "nicknames_jp", default: [], null: false, array: true
t.index ["name_en"], name: "index_summons_on_name_en", opclass: :gin_trgm_ops, using: :gin
end
@ -329,6 +395,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_103037) do
t.boolean "awakening", default: true, null: false
t.boolean "limit", default: false, null: false
t.boolean "ax", default: false, null: false
t.integer "awakening_types", default: [], array: true
t.string "nicknames_en", default: [], null: false, array: true
t.string "nicknames_jp", default: [], null: false, array: true
t.index ["name_en"], name: "index_weapons_on_name_en", opclass: :gin_trgm_ops, using: :gin
end