2013-01-26 07:52:58 -08:00
|
|
|
# encoding=utf-8
|
|
|
|
# ^ to put the regex in utf-8 mode
|
|
|
|
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
class Item
|
|
|
|
module Search
|
|
|
|
class Query
|
2014-02-26 22:21:20 -08:00
|
|
|
def initialize(filters, user, text=nil)
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
@filters = filters
|
2013-01-22 21:52:34 -08:00
|
|
|
@user = user
|
2014-02-26 22:21:20 -08:00
|
|
|
@text = text
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
end
|
|
|
|
|
2023-07-22 18:13:11 -07:00
|
|
|
def results
|
2024-02-20 16:04:41 -08:00
|
|
|
@filters.map(&:to_query).inject(Item.all, &:merge).order(:name)
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
end
|
2014-02-26 21:55:14 -08:00
|
|
|
|
|
|
|
def to_s
|
2014-02-26 22:21:20 -08:00
|
|
|
@text || @filters.map(&:to_s).join(' ')
|
2014-02-26 21:55:14 -08:00
|
|
|
end
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
|
2013-01-26 07:52:58 -08:00
|
|
|
TEXT_FILTER_EXPR = /([+-]?)(?:(\p{Word}+):)?(?:"([^"]+)"|(\S+))/
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
def self.from_text(text, user=nil)
|
|
|
|
filters = []
|
2013-01-22 21:52:34 -08:00
|
|
|
|
2023-07-22 18:13:11 -07:00
|
|
|
text.scan(TEXT_FILTER_EXPR) do |sign, key, quoted_value, unquoted_value|
|
|
|
|
key = 'name' if key.blank?
|
|
|
|
value = quoted_value || unquoted_value
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
is_positive = (sign != '-')
|
|
|
|
|
2024-02-27 15:08:15 -08:00
|
|
|
filter = parse_text_filter(key, value, is_positive, user)
|
2024-02-27 14:43:42 -08:00
|
|
|
filters << filter if filter.present?
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
end
|
|
|
|
|
2014-02-26 22:21:20 -08:00
|
|
|
self.new(filters, user, text)
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
end
|
2014-02-26 21:55:14 -08:00
|
|
|
|
|
|
|
def self.from_params(params, user=nil)
|
2023-07-29 13:14:23 -07:00
|
|
|
filters = []
|
|
|
|
|
|
|
|
params.values.each do |filter_params|
|
|
|
|
key = filter_params[:key]
|
|
|
|
value = filter_params[:value]
|
|
|
|
is_positive = filter_params[:is_positive] != 'false'
|
|
|
|
|
2024-02-27 15:08:15 -08:00
|
|
|
filter = parse_params_filter(key, value, is_positive, user)
|
2024-02-27 14:43:42 -08:00
|
|
|
filters << filter if filter.present?
|
|
|
|
end
|
|
|
|
|
|
|
|
self.new(filters, user)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2024-02-27 15:08:15 -08:00
|
|
|
def self.parse_text_filter(key, value, is_positive, user)
|
2024-02-27 14:43:42 -08:00
|
|
|
case key
|
|
|
|
when 'name'
|
|
|
|
is_positive ?
|
|
|
|
Filter.name_includes(value) :
|
|
|
|
Filter.name_excludes(value)
|
|
|
|
when 'occupies'
|
|
|
|
is_positive ? Filter.occupies(value) : Filter.not_occupies(value)
|
|
|
|
when 'restricts'
|
|
|
|
is_positive ? Filter.restricts(value) : Filter.not_restricts(value)
|
|
|
|
when 'fits'
|
2024-02-27 15:33:08 -08:00
|
|
|
# First, try the `fits:blue-acara` case.
|
2024-02-27 15:51:27 -08:00
|
|
|
# NOTE: This will also work for `fits:"usuki girl-usul"`!
|
2024-06-28 01:32:15 -07:00
|
|
|
match = value.match(/\A([^-]+)-([^-]+)\z/)
|
2024-02-27 14:56:36 -08:00
|
|
|
if match.present?
|
|
|
|
color_name, species_name = match.captures
|
|
|
|
pet_type = load_pet_type_by_name(color_name, species_name)
|
|
|
|
return is_positive ?
|
|
|
|
Filter.fits_pet_type(pet_type, color_name:, species_name:) :
|
|
|
|
Filter.not_fits_pet_type(pet_type, color_name:, species_name:)
|
|
|
|
end
|
2024-02-27 15:33:08 -08:00
|
|
|
|
2024-02-27 15:36:45 -08:00
|
|
|
# Next, try the `fits:alt-style-87305` case.
|
2024-06-28 01:32:15 -07:00
|
|
|
match = value.match(/\Aalt-style-([0-9]+)\z/)
|
2024-02-27 15:36:45 -08:00
|
|
|
if match.present?
|
|
|
|
alt_style_id, = match.captures
|
|
|
|
alt_style = load_alt_style_by_id(alt_style_id)
|
|
|
|
return is_positive ?
|
|
|
|
Filter.fits_alt_style(alt_style) :
|
|
|
|
Filter.not_fits_alt_style(alt_style)
|
|
|
|
end
|
|
|
|
|
2024-02-27 15:33:08 -08:00
|
|
|
# Next, try the `fits:nostalgic-faerie-draik` case.
|
2024-02-27 15:51:27 -08:00
|
|
|
# NOTE: This will also work for `fits:"nostalgic-usuki girl-usul"`!
|
2024-06-28 01:32:15 -07:00
|
|
|
match = value.match(/\A([^-]+)-([^-]+)-([^-]+)\z/)
|
2024-02-27 15:33:08 -08:00
|
|
|
if match.present?
|
|
|
|
series_name, color_name, species_name = match.captures
|
|
|
|
alt_style = load_alt_style_by_name(
|
|
|
|
series_name, color_name, species_name)
|
|
|
|
return is_positive ?
|
|
|
|
Filter.fits_alt_style(alt_style) :
|
|
|
|
Filter.not_fits_alt_style(alt_style)
|
|
|
|
end
|
|
|
|
|
|
|
|
# TODO: We could make `fits:acara` an alias for `species:acara`, or
|
|
|
|
# even the primary syntax?
|
|
|
|
|
|
|
|
# If none of these cases work, raise an error.
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.fits_target", value: value
|
2024-02-27 14:43:42 -08:00
|
|
|
when 'species'
|
|
|
|
begin
|
|
|
|
species = Species.find_by_name!(value)
|
|
|
|
color = Color.find_by_name!('blue')
|
|
|
|
pet_type = PetType.where(color_id: color.id, species_id: species.id).first!
|
|
|
|
rescue ActiveRecord::RecordNotFound
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.species",
|
|
|
|
species_name: value.capitalize
|
2024-02-27 14:43:42 -08:00
|
|
|
end
|
|
|
|
is_positive ?
|
|
|
|
Filter.fits_species(pet_type.body_id, value) :
|
|
|
|
Filter.not_fits_species(pet_type.body_id, value)
|
|
|
|
when 'user'
|
|
|
|
if user.nil?
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_logged_in"
|
2024-02-27 14:43:42 -08:00
|
|
|
end
|
|
|
|
case value
|
|
|
|
when 'owns'
|
|
|
|
is_positive ? Filter.owned_by(user) : Filter.not_owned_by(user)
|
|
|
|
when 'wants'
|
|
|
|
is_positive ? Filter.wanted_by(user) : Filter.not_wanted_by(user)
|
2023-07-29 13:14:23 -07:00
|
|
|
else
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.ownership", keyword: value
|
2023-07-29 13:14:23 -07:00
|
|
|
end
|
2024-02-27 14:43:42 -08:00
|
|
|
when 'is'
|
|
|
|
case value
|
|
|
|
when 'nc'
|
|
|
|
is_positive ? Filter.is_nc : Filter.is_not_nc
|
|
|
|
when 'np'
|
|
|
|
is_positive ? Filter.is_np : Filter.is_not_np
|
|
|
|
when 'pb'
|
|
|
|
is_positive ? Filter.is_pb : Filter.is_not_pb
|
2024-11-19 15:54:55 -08:00
|
|
|
when 'modeled'
|
|
|
|
is_positive ? Filter.is_modeled : Filter.is_not_modeled
|
2024-02-27 14:43:42 -08:00
|
|
|
else
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.label", label: "is:#{value}"
|
2024-02-27 14:43:42 -08:00
|
|
|
end
|
|
|
|
else
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.label", label: key
|
2023-07-29 13:14:23 -07:00
|
|
|
end
|
2024-02-27 14:43:42 -08:00
|
|
|
end
|
2023-07-29 13:14:23 -07:00
|
|
|
|
2024-02-27 15:08:15 -08:00
|
|
|
def self.parse_params_filter(key, value, is_positive, user)
|
2024-02-27 14:43:42 -08:00
|
|
|
case key
|
|
|
|
when 'name'
|
|
|
|
is_positive ?
|
|
|
|
Filter.name_includes(value) :
|
|
|
|
Filter.name_excludes(value)
|
|
|
|
when 'is_nc'
|
|
|
|
is_positive ? Filter.is_nc : Filter.is_not_nc
|
|
|
|
when 'is_pb'
|
|
|
|
is_positive ? Filter.is_pb : Filter.is_not_pb
|
|
|
|
when 'is_np'
|
|
|
|
is_positive ? Filter.is_np : Filter.is_not_np
|
|
|
|
when 'occupied_zone_set_name'
|
|
|
|
is_positive ? Filter.occupies(value) : Filter.not_occupies(value)
|
|
|
|
when 'restricted_zone_set_name'
|
|
|
|
is_positive ? Filter.restricts(value) : Filter.not_restricts(value)
|
|
|
|
when 'fits'
|
|
|
|
if value[:alt_style_id].present?
|
|
|
|
alt_style = load_alt_style_by_id(value[:alt_style_id])
|
|
|
|
is_positive ?
|
|
|
|
Filter.fits_alt_style(alt_style) :
|
|
|
|
Filter.fits_alt_style(alt_style)
|
|
|
|
else
|
|
|
|
pet_type = load_pet_type_by_color_and_species(
|
|
|
|
value[:color_id], value[:species_id])
|
|
|
|
is_positive ?
|
|
|
|
Filter.fits_pet_type(pet_type) :
|
|
|
|
Filter.not_fits_pet_type(pet_type)
|
|
|
|
end
|
|
|
|
when 'user_closet_hanger_ownership'
|
|
|
|
case value
|
|
|
|
when 'true'
|
|
|
|
is_positive ?
|
|
|
|
Filter.owned_by(user) :
|
|
|
|
Filter.not_owned_by(user)
|
|
|
|
when 'false'
|
|
|
|
is_positive ?
|
|
|
|
Filter.wanted_by(user) :
|
|
|
|
Filter.not_wanted_by(user)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
Rails.logger.warn "Ignoring unexpected search filter key: #{key}"
|
|
|
|
end
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
2024-02-23 10:44:50 -08:00
|
|
|
|
2024-02-25 14:46:27 -08:00
|
|
|
def self.load_pet_type_by_name(color_name, species_name)
|
2024-02-23 10:44:50 -08:00
|
|
|
begin
|
|
|
|
PetType.matching_name(color_name, species_name).first!
|
|
|
|
rescue ActiveRecord::RecordNotFound
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.pet_type",
|
|
|
|
name1: color_name.capitalize, name2: species_name.capitalize
|
2024-02-23 10:44:50 -08:00
|
|
|
end
|
|
|
|
end
|
2024-02-25 14:46:27 -08:00
|
|
|
|
|
|
|
def self.load_pet_type_by_color_and_species(color_id, species_id)
|
|
|
|
begin
|
|
|
|
PetType.where(color_id: color_id, species_id: species_id).first!
|
|
|
|
rescue ActiveRecord::RecordNotFound
|
|
|
|
color_name = Color.find(color_id).name rescue "Color #{color_id}"
|
|
|
|
species_name = Species.find(species_id).name rescue "Species #{species_id}"
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.pet_type",
|
|
|
|
name1: color_name.capitalize, name2: species_name.capitalize
|
2024-02-25 14:46:27 -08:00
|
|
|
end
|
|
|
|
end
|
2024-02-27 14:32:54 -08:00
|
|
|
|
2024-02-27 15:33:08 -08:00
|
|
|
def self.load_alt_style_by_name(series_name, color_name, species_name)
|
|
|
|
begin
|
|
|
|
AltStyle.matching_name(series_name, color_name, species_name).first!
|
|
|
|
rescue ActiveRecord::RecordNotFound
|
|
|
|
raise_search_error "not_found.alt_style",
|
|
|
|
filter_text: "#{series_name}-#{color_name}-#{species_name}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-02-27 14:32:54 -08:00
|
|
|
def self.load_alt_style_by_id(alt_style_id)
|
|
|
|
begin
|
|
|
|
AltStyle.find(alt_style_id)
|
2024-02-27 15:33:08 -08:00
|
|
|
rescue ActiveRecord::RecordNotFound
|
2024-02-27 15:03:18 -08:00
|
|
|
raise_search_error "not_found.alt_style",
|
|
|
|
filter_text: "alt-style-#{alt_style_id}"
|
2024-02-27 14:32:54 -08:00
|
|
|
end
|
|
|
|
end
|
2024-02-27 15:03:18 -08:00
|
|
|
|
|
|
|
def self.raise_search_error(kind, ...)
|
|
|
|
raise Item::Search::Error,
|
|
|
|
I18n.translate("items.search.errors.#{kind}", ...)
|
|
|
|
end
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
2014-04-02 18:26:53 -07:00
|
|
|
|
2023-07-22 18:13:11 -07:00
|
|
|
class Error < Exception
|
|
|
|
end
|
2014-04-02 18:26:53 -07:00
|
|
|
|
2023-07-22 18:13:11 -07:00
|
|
|
private
|
2014-02-26 21:55:14 -08:00
|
|
|
|
2023-07-26 11:46:10 -07:00
|
|
|
# A Filter is basically a wrapper for an Item scope, with extra info about
|
|
|
|
# how to convert it into a search query string.
|
|
|
|
class Filter
|
2023-07-29 13:14:23 -07:00
|
|
|
def initialize(query, text)
|
2023-07-26 11:46:10 -07:00
|
|
|
@query = query
|
2023-07-29 13:14:23 -07:00
|
|
|
@text = text
|
2014-02-26 21:55:14 -08:00
|
|
|
end
|
2023-07-22 18:13:11 -07:00
|
|
|
|
|
|
|
def to_query
|
2023-07-26 11:46:10 -07:00
|
|
|
@query
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
end
|
2023-07-22 18:13:11 -07:00
|
|
|
|
|
|
|
def to_s
|
2023-07-29 13:14:23 -07:00
|
|
|
@text
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
|
|
|
|
2023-07-26 11:46:10 -07:00
|
|
|
def inspect
|
|
|
|
"#<#{self.class.name} #{@text.inspect}>"
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
|
|
|
|
2024-02-25 15:00:22 -08:00
|
|
|
def self.name_includes(value)
|
|
|
|
self.new Item.name_includes(value), "#{q value}"
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
|
|
|
|
2024-02-25 15:00:22 -08:00
|
|
|
def self.name_excludes(value)
|
|
|
|
self.new Item.name_excludes(value), "-#{q value}"
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
|
|
|
|
2024-01-23 05:43:00 -08:00
|
|
|
def self.occupies(value)
|
|
|
|
self.new Item.occupies(value), "occupies:#{q value}"
|
2023-07-26 12:28:25 -07:00
|
|
|
end
|
|
|
|
|
2024-01-23 05:43:00 -08:00
|
|
|
def self.not_occupies(value)
|
|
|
|
self.new Item.not_occupies(value), "-occupies:#{q value}"
|
2023-07-26 12:28:25 -07:00
|
|
|
end
|
|
|
|
|
2024-01-23 05:43:00 -08:00
|
|
|
def self.restricts(value)
|
|
|
|
self.new Item.restricts(value), "restricts:#{q value}"
|
2023-07-26 12:41:37 -07:00
|
|
|
end
|
|
|
|
|
2024-01-23 05:43:00 -08:00
|
|
|
def self.not_restricts(value)
|
|
|
|
self.new Item.not_restricts(value), "-restricts:#{q value}"
|
2023-07-26 12:41:37 -07:00
|
|
|
end
|
|
|
|
|
2024-02-27 14:05:37 -08:00
|
|
|
def self.fits_pet_type(pet_type, color_name: nil, species_name: nil)
|
|
|
|
value = pet_type_to_filter_text(pet_type, color_name:, species_name:)
|
|
|
|
self.new Item.fits(pet_type.body_id), "fits:#{q value}"
|
2023-07-28 14:45:10 -07:00
|
|
|
end
|
|
|
|
|
2024-02-27 14:05:37 -08:00
|
|
|
def self.not_fits_pet_type(pet_type, color_name: nil, species_name: nil)
|
|
|
|
value = pet_type_to_filter_text(pet_type, color_name:, species_name:)
|
|
|
|
self.new Item.not_fits(pet_type.body_id), "-fits:#{q value}"
|
2023-08-04 16:54:19 -07:00
|
|
|
end
|
|
|
|
|
2024-02-27 14:32:54 -08:00
|
|
|
def self.fits_alt_style(alt_style)
|
|
|
|
value = alt_style_to_filter_text(alt_style)
|
|
|
|
self.new Item.fits(alt_style.body_id), "fits:#{q value}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.not_fits_alt_style(alt_style)
|
|
|
|
value = alt_style_to_filter_text(alt_style)
|
|
|
|
self.new Item.not_fits(alt_style.body_id), "-fits:#{q value}"
|
|
|
|
end
|
|
|
|
|
2023-08-04 16:54:19 -07:00
|
|
|
def self.fits_species(body_id, species_name)
|
|
|
|
self.new Item.fits(body_id), "species:#{q species_name}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.not_fits_species(body_id, species_name)
|
|
|
|
self.new Item.not_fits(body_id), "-species:#{q species_name}"
|
2023-07-28 14:45:10 -07:00
|
|
|
end
|
|
|
|
|
2023-07-29 12:48:45 -07:00
|
|
|
def self.owned_by(user)
|
2023-07-28 15:06:33 -07:00
|
|
|
self.new user.owned_items, 'user:owns'
|
|
|
|
end
|
|
|
|
|
2023-07-29 12:48:45 -07:00
|
|
|
def self.not_owned_by(user)
|
|
|
|
self.new user.unowned_items, 'user:owns'
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.wanted_by(user)
|
2023-07-28 15:06:33 -07:00
|
|
|
self.new user.wanted_items, 'user:wants'
|
|
|
|
end
|
|
|
|
|
2023-07-29 12:48:45 -07:00
|
|
|
def self.not_wanted_by(user)
|
|
|
|
self.new user.unwanted_items, 'user:wants'
|
|
|
|
end
|
|
|
|
|
2023-07-26 11:46:10 -07:00
|
|
|
def self.is_nc
|
|
|
|
self.new Item.is_nc, 'is:nc'
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
|
|
|
|
2023-07-26 11:46:10 -07:00
|
|
|
def self.is_not_nc
|
2024-02-25 12:57:04 -08:00
|
|
|
self.new Item.is_not_nc, '-is:nc'
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
|
|
|
|
2023-07-26 11:46:10 -07:00
|
|
|
def self.is_np
|
|
|
|
self.new Item.is_np, 'is:np'
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.is_not_np
|
2024-02-25 12:57:04 -08:00
|
|
|
self.new Item.is_not_np, '-is:np'
|
2023-07-22 18:13:11 -07:00
|
|
|
end
|
2023-07-26 11:51:52 -07:00
|
|
|
|
|
|
|
def self.is_pb
|
|
|
|
self.new Item.is_pb, 'is:pb'
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.is_not_pb
|
|
|
|
self.new Item.is_not_pb, '-is:pb'
|
|
|
|
end
|
2023-07-28 14:45:10 -07:00
|
|
|
|
2024-11-19 15:54:55 -08:00
|
|
|
def self.is_modeled
|
|
|
|
self.new Item.is_modeled, 'is:modeled'
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.is_not_modeled
|
|
|
|
self.new Item.is_not_modeled, '-is:modeled'
|
|
|
|
end
|
|
|
|
|
2023-07-28 14:45:10 -07:00
|
|
|
private
|
|
|
|
|
2023-08-04 16:54:19 -07:00
|
|
|
# Add quotes around the value, if needed.
|
|
|
|
def self.q(value)
|
|
|
|
/\s/.match(value) ? '"' + value + '"' : value
|
|
|
|
end
|
2024-02-27 14:05:37 -08:00
|
|
|
|
|
|
|
def self.pet_type_to_filter_text(pet_type, color_name: nil, species_name: nil)
|
|
|
|
# Load the color & species name if needed, or use them from the params
|
|
|
|
# if already known (e.g. from parsing a "fits:blue-acara" text query).
|
|
|
|
color_name ||= pet_type.color.name
|
|
|
|
species_name ||= pet_type.species.name
|
|
|
|
|
|
|
|
# NOTE: Some color syntaxes are weird, like `fits:"polka dot-aisha"`!
|
2024-02-27 14:32:54 -08:00
|
|
|
"#{color_name}-#{species_name}".downcase
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.alt_style_to_filter_text(alt_style)
|
2024-02-27 15:51:27 -08:00
|
|
|
# If the real series name has been set in the database by support
|
|
|
|
# staff, use that for the canonical filter text for this alt style.
|
|
|
|
# Otherwise, represent this alt style by ID.
|
2024-10-18 17:13:16 -07:00
|
|
|
if alt_style.real_series_name?
|
2024-02-27 15:51:27 -08:00
|
|
|
series_name = alt_style.series_name.downcase
|
|
|
|
color_name = alt_style.color.name.downcase
|
|
|
|
species_name = alt_style.species.name.downcase
|
|
|
|
"#{series_name}-#{color_name}-#{species_name}"
|
|
|
|
else
|
|
|
|
"alt-style-#{alt_style.id}"
|
|
|
|
end
|
2024-02-27 14:05:37 -08:00
|
|
|
end
|
globalized search first draft
Confirmed features:
* Output (retrieval, sorting, etc.)
* Name (positive and negative, but new behavior)
* Flags (positive and negative)
Planned features:
* users:owns, user:wants
Known issues:
* Sets are broken
* Don't render properly
* Shouldn't actually be done as joined sets, anyway, since
we actually want (set1_zone1 OR set1_zone2) AND
(set2_zone1 OR set2_zone2), which will require breaking
it into multiple terms queries.
* Name has regressed: ignores phrases, doesn't require *all*
words. While we're breaking sets into multiple queries,
maybe we'll do something similar for name. In fact, we
really kinda have to if we're gonna keep sorting by name,
since "straw hat" returns all hats. Eww.
2013-01-18 21:23:37 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|