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
|
|
|
|
@filters.map(&:to_query).inject(Item.all, &:merge).
|
|
|
|
alphabetize_by_translations(Query.locale)
|
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
|
2014-02-26 22:21:20 -08:00
|
|
|
|
2023-07-22 18:13:11 -07:00
|
|
|
def self.locale
|
|
|
|
(I18n.fallbacks[I18n.locale] &
|
|
|
|
I18n.locales_with_neopets_language_code).first
|
|
|
|
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 != '-')
|
|
|
|
|
2023-07-22 18:13:11 -07:00
|
|
|
case key
|
|
|
|
when 'name'
|
|
|
|
filters << NameFilter.new(value, locale, is_positive)
|
|
|
|
when 'is'
|
|
|
|
case value
|
|
|
|
when 'nc'
|
|
|
|
filters << NCFilter.new(is_positive)
|
|
|
|
when 'np'
|
|
|
|
filters << NPFilter.new(is_positive)
|
|
|
|
else
|
|
|
|
message = I18n.translate('items.search.errors.not_found.label',
|
2023-07-26 11:06:36 -07:00
|
|
|
:label => "is:#{value}")
|
2023-07-22 18:13:11 -07:00
|
|
|
raise Item::Search::Error, message
|
2013-01-26 07:52:58 -08:00
|
|
|
end
|
|
|
|
else
|
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
|
|
|
message = I18n.translate('items.search.errors.not_found.label',
|
2023-07-22 18:13:11 -07:00
|
|
|
:label => key)
|
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
|
|
|
raise Item::Search::Error, message
|
|
|
|
end
|
|
|
|
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-22 18:13:11 -07:00
|
|
|
raise NotImplementedError, "TODO: Reimplemented Advanced Search"
|
|
|
|
end
|
|
|
|
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-22 18:13:11 -07:00
|
|
|
class NameFilter
|
|
|
|
def initialize(value, locale, is_positive)
|
|
|
|
@value = value
|
|
|
|
@locale = locale
|
|
|
|
@is_positive = is_positive
|
2014-02-26 21:55:14 -08:00
|
|
|
end
|
2023-07-22 18:13:11 -07:00
|
|
|
|
|
|
|
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) + '%')
|
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
|
2023-07-22 18:13:11 -07:00
|
|
|
|
|
|
|
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
|
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
|