Start building new item search
Just name field right now, more to come! A lot deleted lol
This commit is contained in:
parent
7641d2325a
commit
20bedf8f59
10 changed files with 110 additions and 439 deletions
|
@ -13,15 +13,15 @@ class ItemsController < ApplicationController
|
||||||
end
|
end
|
||||||
# Note that we sort by name by hand, since we might have to use
|
# Note that we sort by name by hand, since we might have to use
|
||||||
# fallbacks after the fact
|
# fallbacks after the fact
|
||||||
@items = @query.paginate(page: params[:page], per_page: per_page)
|
@items = @query.results.includes(:translations).
|
||||||
|
paginate(page: params[:page], per_page: per_page)
|
||||||
assign_closeted!
|
assign_closeted!
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
@campaign = Campaign.current rescue nil
|
@campaign = Campaign.current rescue nil
|
||||||
if @items.total_count == 1
|
if @items.count == 1
|
||||||
redirect_to @items.first
|
redirect_to @items.first
|
||||||
else
|
else
|
||||||
@items.prepare_partial(:item_link_partial)
|
|
||||||
render
|
render
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,11 @@ class Item < ActiveRecord::Base
|
||||||
cattr_reader :per_page
|
cattr_reader :per_page
|
||||||
@@per_page = 30
|
@@per_page = 30
|
||||||
|
|
||||||
scope :alphabetize_by_translations, -> {
|
scope :alphabetize_by_translations, ->(locale) {
|
||||||
|
locale = locale or I18n.locale
|
||||||
it = Item::Translation.arel_table
|
it = Item::Translation.arel_table
|
||||||
order(it[:name])
|
joins(:translations).where(it[:locale].eq('en')).
|
||||||
|
order(it[:name])
|
||||||
}
|
}
|
||||||
|
|
||||||
scope :join_swf_assets, -> { joins(:swf_assets).group(arel_table[:id]) }
|
scope :join_swf_assets, -> { joins(:swf_assets).group(arel_table[:id]) }
|
||||||
|
@ -45,6 +47,17 @@ class Item < ActiveRecord::Base
|
||||||
|
|
||||||
scope :with_closet_hangers, -> { joins(:closet_hangers) }
|
scope :with_closet_hangers, -> { joins(:closet_hangers) }
|
||||||
|
|
||||||
|
scope :is_nc, -> {
|
||||||
|
i = Item.arel_table
|
||||||
|
condition = i[:rarity_index].in(Item::NCRarities).or(i[:is_manually_nc])
|
||||||
|
where(condition)
|
||||||
|
}
|
||||||
|
scope :is_np, -> {
|
||||||
|
i = Item.arel_table
|
||||||
|
condition = i[:rarity_index].in(Item::NCRarities).or(i[:is_manually_nc])
|
||||||
|
where(condition.not)
|
||||||
|
}
|
||||||
|
|
||||||
def closeted?
|
def closeted?
|
||||||
@owned || @wanted
|
@owned || @wanted
|
||||||
end
|
end
|
||||||
|
@ -551,6 +564,12 @@ class Item < ActiveRecord::Base
|
||||||
Item::ProxyArray.new(ids)
|
Item::ProxyArray.new(ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: Copied from modern Rails source, can delete once we're there!
|
||||||
|
def self.sanitize_sql_like(string, escape_character = "\\")
|
||||||
|
pattern = Regexp.union(escape_character, "%", "_")
|
||||||
|
string.gsub(pattern) { |x| [escape_character, x].join }
|
||||||
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
MALL_HOST = 'ncmall.neopets.com'
|
MALL_HOST = 'ncmall.neopets.com'
|
||||||
MALL_MAIN_PATH = '/mall/shop.phtml'
|
MALL_MAIN_PATH = '/mall/shop.phtml'
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
class Item
|
|
||||||
module Search
|
|
||||||
class Contradiction < Error
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,7 +1,6 @@
|
||||||
class Item
|
class Item
|
||||||
module Search
|
module Search
|
||||||
class Error < RuntimeError
|
class Error < Exception
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,11 +0,0 @@
|
||||||
class Item
|
|
||||||
module Search
|
|
||||||
class Field
|
|
||||||
attr_reader :key
|
|
||||||
|
|
||||||
def initialize(key)
|
|
||||||
@key = key
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,20 +0,0 @@
|
||||||
class Item
|
|
||||||
module Search
|
|
||||||
module Fields
|
|
||||||
class Flag < Field
|
|
||||||
def <<(filter)
|
|
||||||
if @value.nil?
|
|
||||||
@value = filter.positive?
|
|
||||||
elsif @value != filter.positive?
|
|
||||||
raise Item::Search::Contradiction,
|
|
||||||
"flag #{key} both positive and negative"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_flex_params
|
|
||||||
{key => @value}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,34 +0,0 @@
|
||||||
class Item
|
|
||||||
module Search
|
|
||||||
module Fields
|
|
||||||
class SetField < Field
|
|
||||||
def initialize(*args)
|
|
||||||
super(*args)
|
|
||||||
@values = {true => Set.new, false => Set.new}
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(filter)
|
|
||||||
if @values[!filter.positive?].include?(filter.value)
|
|
||||||
raise Item::Search::Contradiction,
|
|
||||||
"positive #{key} and negative #{key} both contain #{filter.value}"
|
|
||||||
end
|
|
||||||
|
|
||||||
@values[filter.positive?] << filter.value
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_flex_params
|
|
||||||
{
|
|
||||||
:"_#{key}s" => nil_if_empty(@values[true]),
|
|
||||||
:"_negative_#{key}s" => nil_if_empty(@values[false])
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def nil_if_empty(set)
|
|
||||||
set.map { |value| {key => value} } unless set.empty?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,29 +0,0 @@
|
||||||
class Item
|
|
||||||
module Search
|
|
||||||
module Fields
|
|
||||||
class TextField < Field
|
|
||||||
def initialize(*args)
|
|
||||||
super(*args)
|
|
||||||
@values = {true => '', false => ''}
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(filter)
|
|
||||||
@values[filter.positive?] << (filter.value + ' ')
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_flex_params
|
|
||||||
{
|
|
||||||
key => nil_if_empty(@values[true]),
|
|
||||||
:"negative_#{key}" => nil_if_empty(@values[false])
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def nil_if_empty(str)
|
|
||||||
str unless str.empty?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,54 +0,0 @@
|
||||||
class Item
|
|
||||||
module Search
|
|
||||||
class Filter
|
|
||||||
attr_reader :key, :value
|
|
||||||
|
|
||||||
def initialize(key, value, is_positive)
|
|
||||||
@key = key
|
|
||||||
@value = value
|
|
||||||
@is_positive = is_positive
|
|
||||||
end
|
|
||||||
|
|
||||||
def positive?
|
|
||||||
@is_positive
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
sign = positive? ? '' : '-'
|
|
||||||
key_str = @key.to_s
|
|
||||||
if key_str.start_with?('is_')
|
|
||||||
is_label = I18n.translate("items.search.flag_keywords.is")
|
|
||||||
"#{sign}#{is_label}:#{label}"
|
|
||||||
elsif key_str.start_with?('user_')
|
|
||||||
user_label = I18n.translate("items.search.labels.user_closet_hanger_ownership")
|
|
||||||
value = Query::REVERSE_RESOURCE_FINDERS[:ownership].call(@value)
|
|
||||||
"#{sign}#{user_label}:#{value}"
|
|
||||||
else
|
|
||||||
if Query::TEXT_QUERY_RESOURCE_TYPES_BY_KEY.include?(@key)
|
|
||||||
resource_type = Query::TEXT_QUERY_RESOURCE_TYPES_BY_KEY[@key]
|
|
||||||
reverse_finder = Query::REVERSE_RESOURCE_FINDERS[resource_type]
|
|
||||||
resource_value = reverse_finder.call(@value)
|
|
||||||
else
|
|
||||||
resource_value = @value
|
|
||||||
end
|
|
||||||
if resource_value.include?(' ')
|
|
||||||
quoted_value = resource_value.inspect
|
|
||||||
else
|
|
||||||
quoted_value = resource_value
|
|
||||||
end
|
|
||||||
if @key == :name
|
|
||||||
"#{sign}#{quoted_value}"
|
|
||||||
else
|
|
||||||
"#{sign}#{label}:#{quoted_value}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def label
|
|
||||||
I18n.translate("items.search.labels.#{@key}").split(',').first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -4,315 +4,123 @@
|
||||||
class Item
|
class Item
|
||||||
module Search
|
module Search
|
||||||
class Query
|
class Query
|
||||||
FIELD_CLASSES = {
|
|
||||||
:is_nc => Fields::Flag,
|
|
||||||
:is_pb => Fields::Flag,
|
|
||||||
:species_support_id => Fields::SetField,
|
|
||||||
:occupied_zone_id => Fields::SetField,
|
|
||||||
:restricted_zone_id => Fields::SetField,
|
|
||||||
:name => Fields::SetField,
|
|
||||||
:user_closet_hanger_ownership => Fields::SetField,
|
|
||||||
:fits_pet_type => Fields::SetField
|
|
||||||
}
|
|
||||||
FIELD_KEYS = FIELD_CLASSES.keys
|
|
||||||
|
|
||||||
def initialize(filters, user, text=nil)
|
def initialize(filters, user, text=nil)
|
||||||
@filters = filters
|
@filters = filters
|
||||||
@user = user
|
@user = user
|
||||||
@text = text
|
@text = text
|
||||||
end
|
end
|
||||||
|
|
||||||
def fields
|
def results
|
||||||
initial_fields.tap do |fields|
|
@filters.map(&:to_query).inject(Item.all, &:merge).
|
||||||
@filters.each { |filter| fields[filter.key] << filter }
|
alphabetize_by_translations(Query.locale)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_flex_params
|
|
||||||
fields.values.map(&:to_flex_params).inject(&:merge)
|
|
||||||
end
|
|
||||||
|
|
||||||
def paginate(options={})
|
|
||||||
begin
|
|
||||||
flex_params = self.to_flex_params
|
|
||||||
rescue Item::Search::Contradiction
|
|
||||||
# If we have a contradictory query, no need to raise a stink about
|
|
||||||
# it, but no need to actually run a search, either.
|
|
||||||
return Item.build_proxies([])
|
|
||||||
end
|
|
||||||
|
|
||||||
final_flex_params = {
|
|
||||||
:page => (options[:page] || 1),
|
|
||||||
:size => (options[:per_page] || 30),
|
|
||||||
:type => 'item'
|
|
||||||
}.merge(flex_params)
|
|
||||||
|
|
||||||
locales = I18n.fallbacks[I18n.locale] &
|
|
||||||
I18n.locales_with_neopets_language_code
|
|
||||||
final_flex_params[:locale] = locales.first
|
|
||||||
|
|
||||||
# Extend the names/negative_names queries with the corresponding
|
|
||||||
# localalized field names.
|
|
||||||
if final_flex_params[:_names] || final_flex_params[:_negative_names]
|
|
||||||
locale_entries = locales.map do |locale|
|
|
||||||
boost = (locale == I18n.locale) ? 4 : 1
|
|
||||||
"name.#{locale}^#{boost}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# We *could* have set _name_locales once as a partial, but Flex won't
|
|
||||||
# let us call partials from inside other partials. Whatever. Assign
|
|
||||||
# it to each name entry instead. I also feel bad doing this
|
|
||||||
# afterwards, since it's kinda the field's job to return proper flex
|
|
||||||
# params, but that's a refactor for another day.
|
|
||||||
valid_name_lengths = (3..16)
|
|
||||||
[:_names, :_negative_names].each do |key|
|
|
||||||
if final_flex_params[key]
|
|
||||||
# This part is also kinda weak. Oh well. Maybe we need
|
|
||||||
# NGramField that inherits from SetField while also applying
|
|
||||||
# these restrictions? Remove all name filters that are too
|
|
||||||
# small or too large.
|
|
||||||
final_flex_params[key].select! do |name_query|
|
|
||||||
valid_name_lengths.include?(name_query[:name].length)
|
|
||||||
end
|
|
||||||
|
|
||||||
final_flex_params[key].each do |name_query|
|
|
||||||
name_query[:fields] = locale_entries
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Okay, yeah, looks like this really does deserve a refactor, like
|
|
||||||
# _names and _negative_names do. (Or Flex could just make all variables
|
|
||||||
# accessible from partials... hint, hint)
|
|
||||||
[:_user_closet_hanger_ownerships, :_negative_user_closet_hanger_ownerships].each do |key|
|
|
||||||
if final_flex_params[key]
|
|
||||||
Item::Search.error 'not_logged_in' unless @user
|
|
||||||
|
|
||||||
final_flex_params[key].each do |entry|
|
|
||||||
entry[:user_id] = @user.id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
[:_fits_pet_types, :_negative_fits_pet_types].each do |key|
|
|
||||||
if final_flex_params[key]
|
|
||||||
final_flex_params[key].each do |entry|
|
|
||||||
# These are pet types at first. Yikes, dem hax.
|
|
||||||
entry[:body_id] = entry[:fits_pet_type].body_id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
result = FlexSearch.item_search(final_flex_params)
|
|
||||||
|
|
||||||
result.proxied_collection
|
|
||||||
end
|
|
||||||
|
|
||||||
# Load the text query labels from I18n, so that when we see, say,
|
|
||||||
# the filter "species:acara", we know it means species_support_id.
|
|
||||||
TEXT_KEYS_BY_LABEL = {}
|
|
||||||
IS_KEYWORDS = {}
|
|
||||||
OWNERSHIP_KEYWORDS = {}
|
|
||||||
I18n.available_locales.each do |locale|
|
|
||||||
TEXT_KEYS_BY_LABEL[locale] = {}
|
|
||||||
IS_KEYWORDS[locale] = Set.new
|
|
||||||
OWNERSHIP_KEYWORDS[locale] = {}
|
|
||||||
|
|
||||||
I18n.fallbacks[locale].each do |fallback|
|
|
||||||
FIELD_KEYS.each do |key|
|
|
||||||
# A locale can specify multiple labels for a key by separating by
|
|
||||||
# commas: "occupies,zone,type"
|
|
||||||
labels = I18n.translate("items.search.labels.#{key}",
|
|
||||||
:locale => fallback).split(',')
|
|
||||||
|
|
||||||
labels.each do |label|
|
|
||||||
plain_label = label.parameterize # 'é' => 'e'
|
|
||||||
TEXT_KEYS_BY_LABEL[locale][plain_label] = key
|
|
||||||
end
|
|
||||||
|
|
||||||
is_keyword = I18n.translate('items.search.flag_keywords.is',
|
|
||||||
:locale => fallback)
|
|
||||||
IS_KEYWORDS[locale] << is_keyword.parameterize
|
|
||||||
|
|
||||||
{:owns => true, :wants => false}.each do |key, value|
|
|
||||||
translated_key = I18n.translate("items.search.labels.user_#{key}",
|
|
||||||
:locale => fallback)
|
|
||||||
OWNERSHIP_KEYWORDS[locale][translated_key] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
@text || @filters.map(&:to_s).join(' ')
|
@text || @filters.map(&:to_s).join(' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
TEXT_QUERY_RESOURCE_FINDERS = {
|
|
||||||
:species => lambda { |name|
|
|
||||||
species = Species.find_by_name(name)
|
|
||||||
unless species
|
|
||||||
Item::Search.error 'not_found.species', :species_name => name
|
|
||||||
end
|
|
||||||
species.id
|
|
||||||
},
|
|
||||||
:zone => lambda { |label|
|
|
||||||
zone_set = Zone.with_plain_label(label)
|
|
||||||
if zone_set.empty?
|
|
||||||
Item::Search.error 'not_found.zone', :zone_name => label
|
|
||||||
end
|
|
||||||
zone_set.map(&:id)
|
|
||||||
},
|
|
||||||
:ownership => lambda { |keyword|
|
|
||||||
OWNERSHIP_KEYWORDS[I18n.locale][keyword].tap do |value|
|
|
||||||
if value.nil?
|
|
||||||
Item::Search.error 'not_found.ownership', :keyword => keyword
|
|
||||||
end
|
|
||||||
end
|
|
||||||
},
|
|
||||||
:pet_type => lambda { |keyword|
|
|
||||||
name1, dash, name2 = keyword.rpartition('-')
|
|
||||||
[[name1, name2], [name2, name1]].each do |species_name, color_name|
|
|
||||||
species = Species.find_by_name(species_name)
|
|
||||||
next if species.nil?
|
|
||||||
color = Color.find_by_name(color_name)
|
|
||||||
next if color.nil?
|
|
||||||
pet_type = PetType.find_by_species_id_and_color_id(species, color)
|
|
||||||
return pet_type if pet_type.present?
|
|
||||||
end
|
|
||||||
Item::Search.error 'not_found.pet_type',
|
|
||||||
name1: name1.humanize, name2: name2.humanize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
REVERSE_RESOURCE_FINDERS = {
|
def self.locale
|
||||||
species: lambda { |id|
|
(I18n.fallbacks[I18n.locale] &
|
||||||
Species.find(id).name
|
I18n.locales_with_neopets_language_code).first
|
||||||
},
|
end
|
||||||
zone: lambda { |ids|
|
|
||||||
Zone.find(ids.first).plain_label
|
|
||||||
},
|
|
||||||
ownership: lambda { |owned|
|
|
||||||
owned_key = owned ? 'owns' : 'wants'
|
|
||||||
I18n.translate("items.search.labels.user_#{owned_key}")
|
|
||||||
},
|
|
||||||
pet_type: lambda { |pet_type|
|
|
||||||
"#{pet_type.color.name}-#{pet_type.species.name}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEXT_QUERY_RESOURCE_TYPES_BY_KEY = {
|
|
||||||
:species_support_id => :species,
|
|
||||||
:occupied_zone_id => :zone,
|
|
||||||
:restricted_zone_id => :zone,
|
|
||||||
:user_closet_hanger_ownership => :ownership,
|
|
||||||
:fits_pet_type => :pet_type
|
|
||||||
}
|
|
||||||
|
|
||||||
TEXT_FILTER_EXPR = /([+-]?)(?:(\p{Word}+):)?(?:"([^"]+)"|(\S+))/
|
TEXT_FILTER_EXPR = /([+-]?)(?:(\p{Word}+):)?(?:"([^"]+)"|(\S+))/
|
||||||
def self.from_text(text, user=nil)
|
def self.from_text(text, user=nil)
|
||||||
filters = []
|
filters = []
|
||||||
|
|
||||||
text.scan(TEXT_FILTER_EXPR) do |sign, label, quoted_value, unquoted_value|
|
text.scan(TEXT_FILTER_EXPR) do |sign, key, quoted_value, unquoted_value|
|
||||||
raw_value = quoted_value || unquoted_value
|
key = 'name' if key.blank?
|
||||||
|
value = quoted_value || unquoted_value
|
||||||
is_positive = (sign != '-')
|
is_positive = (sign != '-')
|
||||||
|
|
||||||
Rails.logger.debug(label.inspect)
|
case key
|
||||||
Rails.logger.debug(TEXT_KEYS_BY_LABEL[I18n.locale].inspect)
|
when 'name'
|
||||||
Rails.logger.debug(IS_KEYWORDS[I18n.locale].inspect)
|
filters << NameFilter.new(value, locale, is_positive)
|
||||||
|
when 'is'
|
||||||
if label
|
case value
|
||||||
plain_label = label.parameterize
|
when 'nc'
|
||||||
|
filters << NCFilter.new(is_positive)
|
||||||
if IS_KEYWORDS[I18n.locale].include?(plain_label)
|
when 'np'
|
||||||
# is-filters are weird. "-is:nc" is transposed to something more
|
filters << NPFilter.new(is_positive)
|
||||||
# like "-nc:<nil>", then it's translated into a negative "is_nc"
|
else
|
||||||
# flag. Fun fact: "nc:foobar" and "-nc:foobar" also work. A bonus,
|
message = I18n.translate('items.search.errors.not_found.label',
|
||||||
# I guess. There's probably a good way to refactor this to avoid
|
:label => value)
|
||||||
# the unintended bonus syntax, but this is a darn good cheap
|
raise Item::Search::Error, message
|
||||||
# technique for the time being.
|
|
||||||
label = raw_value
|
|
||||||
plain_label = raw_value.parameterize
|
|
||||||
raw_value = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
key = TEXT_KEYS_BY_LABEL[I18n.locale][plain_label]
|
|
||||||
else
|
else
|
||||||
key = :name
|
|
||||||
end
|
|
||||||
|
|
||||||
if key.nil?
|
|
||||||
message = I18n.translate('items.search.errors.not_found.label',
|
message = I18n.translate('items.search.errors.not_found.label',
|
||||||
:label => label)
|
:label => key)
|
||||||
raise Item::Search::Error, message
|
raise Item::Search::Error, message
|
||||||
end
|
end
|
||||||
|
|
||||||
if (!Flex::Configuration.hangers_enabled &&
|
|
||||||
key == :user_closet_hanger_ownership)
|
|
||||||
Item::Search.error 'user_filters_disabled'
|
|
||||||
end
|
|
||||||
|
|
||||||
if TEXT_QUERY_RESOURCE_TYPES_BY_KEY.has_key?(key)
|
|
||||||
resource_type = TEXT_QUERY_RESOURCE_TYPES_BY_KEY[key]
|
|
||||||
finder = TEXT_QUERY_RESOURCE_FINDERS[resource_type]
|
|
||||||
value = finder.call(raw_value)
|
|
||||||
else
|
|
||||||
value = raw_value
|
|
||||||
end
|
|
||||||
|
|
||||||
filters << Filter.new(key, value, is_positive)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self.new(filters, user, text)
|
self.new(filters, user, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.from_params(params, user=nil)
|
def self.from_params(params, user=nil)
|
||||||
filters = params.values.map { |filter_params|
|
raise NotImplementedError, "TODO: Reimplemented Advanced Search"
|
||||||
if filter_params.has_key?(:key)
|
|
||||||
key = filter_params[:key].to_sym
|
|
||||||
value = filter_params[:value]
|
|
||||||
|
|
||||||
# Ew, the hackiest of hacks!
|
|
||||||
if key == :occupied_zone_set_name
|
|
||||||
key = :occupied_zone_id
|
|
||||||
value = TEXT_QUERY_RESOURCE_FINDERS[:zone].call(value)
|
|
||||||
elsif key == :restricted_zone_set_name
|
|
||||||
key = :restricted_zone_id
|
|
||||||
value = TEXT_QUERY_RESOURCE_FINDERS[:zone].call(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
if FIELD_KEYS.include?(key)
|
|
||||||
# Ugh, this bit feels so hacky :P
|
|
||||||
if key == :user_closet_hanger_ownership
|
|
||||||
value = (value == 'true')
|
|
||||||
end
|
|
||||||
if key == :fits_pet_type
|
|
||||||
begin
|
|
||||||
value = PetType.find(value)
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
Item::Search.error 'not_found.pet_type_id', id: value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
is_positive = filter_params[:is_positive] == 'true'
|
|
||||||
Filter.new(key, value, is_positive)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}.compact
|
|
||||||
|
|
||||||
self.new(filters, user)
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
private
|
|
||||||
|
class Error < Exception
|
||||||
# The fields start out empty, then have the filters inserted into 'em,
|
end
|
||||||
# so that the fields can validate and aggregate their requirements.
|
|
||||||
def initial_fields
|
private
|
||||||
{}.tap do |fields|
|
|
||||||
FIELD_CLASSES.map do |key, klass|
|
class NameFilter
|
||||||
fields[key] = klass.new(key)
|
def initialize(value, locale, is_positive)
|
||||||
end
|
@value = value
|
||||||
|
@locale = locale
|
||||||
|
@is_positive = is_positive
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_query
|
||||||
|
base = Item.joins(:translations).where('locale = ?', @locale)
|
||||||
|
if @is_positive
|
||||||
|
base.where('name LIKE ?', '%' + Item.sanitize_sql_like(@value) + '%')
|
||||||
|
else
|
||||||
|
base.where('name NOT LIKE ?', '%' + Item.sanitize_sql_like(@value) + '%')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
sign = @is_positive ? '' : '-'
|
||||||
|
if /\s/.match(@value)
|
||||||
|
sign + '"' + @value + '"'
|
||||||
|
else
|
||||||
|
sign + @value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NCFilter
|
||||||
|
def initialize(is_positive)
|
||||||
|
@is_positive = is_positive
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_query
|
||||||
|
@is_positive ? Item.is_nc : Item.is_np
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
sign = @is_positive ? '' : '-'
|
||||||
|
sign + 'is:nc'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NPFilter
|
||||||
|
def initialize(is_positive)
|
||||||
|
@is_positive = is_positive
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_query
|
||||||
|
@is_positive ? Item.is_np : Item.is_nc
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
sign = @is_positive ? '' : '-'
|
||||||
|
sign + 'is:np'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue