forked from OpenNeo/impress
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.
This commit is contained in:
parent
c9ae7155b1
commit
6e09b8bc10
71 changed files with 594 additions and 43 deletions
7
Gemfile
7
Gemfile
|
@ -43,7 +43,7 @@ gem 'neopets', :git => 'git://github.com/matchu/neopets.git'
|
|||
|
||||
gem "mini_magick", "~> 3.4"
|
||||
|
||||
gem "fog", "~> 1.1.2"
|
||||
gem "fog", "~> 1.8.0"
|
||||
gem "carrierwave", "~> 0.5.8"
|
||||
|
||||
gem "parallel", "~> 0.5.17"
|
||||
|
@ -51,6 +51,11 @@ gem "parallel", "~> 0.5.17"
|
|||
gem "http_accept_language", :git => "git://github.com/iain/http_accept_language.git"
|
||||
gem "globalize3"
|
||||
|
||||
# My flex branch fixes a minor pagination bug. Once it's merged into the
|
||||
# original gem, we can switch back.
|
||||
gem "flex", :require => "flex/rails", :git => "git@github.com:matchu/flex.git"
|
||||
gem "patron", "~> 0.4.18"
|
||||
|
||||
group :development do
|
||||
gem "bullet", "~> 4.1.5"
|
||||
end
|
||||
|
|
96
Gemfile.lock
96
Gemfile.lock
|
@ -1,8 +1,8 @@
|
|||
GIT
|
||||
remote: git://github.com/eventmachine/eventmachine.git
|
||||
revision: 69151c3ebb3e4ecf2bb9b6e2fab2022dc34f8541
|
||||
revision: d7c8a14dc494193a775add4b16c1e303cab5b285
|
||||
specs:
|
||||
eventmachine (1.0.0.beta.4)
|
||||
eventmachine (1.0.0)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/iain/http_accept_language.git
|
||||
|
@ -12,9 +12,9 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/igrigorik/em-http-request.git
|
||||
revision: ce50f322ce08d43a4a747cf333ea576765d764c4
|
||||
revision: 322f2273fa7ea07c1eeb92755bf67f1f05058e54
|
||||
specs:
|
||||
em-http-request (1.0.1)
|
||||
em-http-request (1.0.3)
|
||||
addressable (>= 2.2.3)
|
||||
cookiejar
|
||||
em-socksify
|
||||
|
@ -30,21 +30,21 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/igrigorik/em-synchrony.git
|
||||
revision: c7209a58f9eb92e1dc81fb141297f9f257c2fdcb
|
||||
revision: fe592a4b9b5345bca329477cb8f2f8d186b6fc7f
|
||||
specs:
|
||||
em-synchrony (1.0.0)
|
||||
em-synchrony (1.0.2)
|
||||
eventmachine (>= 1.0.0.beta.1)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/matchu/neopets.git
|
||||
revision: fe694681a302243d2937e811355473f358035403
|
||||
revision: d33aaf63d4617d9236ef0d99452b3bdc577cbc8e
|
||||
specs:
|
||||
neopets (0.0.2)
|
||||
neopets (0.1.0)
|
||||
nokogiri (~> 1.5.2)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/oldmoe/mysqlplus.git
|
||||
revision: 3dbaa7c00ff0bb75ad9538cdef176c72de35d231
|
||||
revision: f07936d2eb9b0893994ed99fe82267b5e7770d06
|
||||
specs:
|
||||
mysqlplus (0.1.1)
|
||||
|
||||
|
@ -54,6 +54,15 @@ GIT
|
|||
specs:
|
||||
RocketAMF (1.0.0)
|
||||
|
||||
GIT
|
||||
remote: git@github.com:matchu/flex.git
|
||||
revision: d62f508f795ecdbb383f406865daa72368b43ba5
|
||||
specs:
|
||||
flex (0.4.1)
|
||||
multi_json (~> 1.3.4)
|
||||
progressbar (~> 0.11.0)
|
||||
prompter (~> 0.1.5)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
|
@ -88,17 +97,17 @@ GEM
|
|||
activemodel (= 3.0.19)
|
||||
activesupport (= 3.0.19)
|
||||
activesupport (3.0.19)
|
||||
addressable (2.2.6)
|
||||
addressable (2.3.2)
|
||||
arel (2.0.10)
|
||||
bcrypt-ruby (2.1.4)
|
||||
builder (2.1.2)
|
||||
bullet (4.1.5)
|
||||
bullet (4.1.6)
|
||||
uniform_notifier (~> 1.0.0)
|
||||
carrierwave (0.5.8)
|
||||
activesupport (~> 3.0)
|
||||
character-encodings (0.4.1)
|
||||
chronic (0.6.7)
|
||||
closure-compiler (1.1.4)
|
||||
closure-compiler (1.1.8)
|
||||
compass (0.10.6)
|
||||
haml (>= 3.0.4)
|
||||
cookiejar (0.3.0)
|
||||
|
@ -106,27 +115,28 @@ GEM
|
|||
bcrypt-ruby (~> 2.1.2)
|
||||
warden (~> 1.0.2)
|
||||
diff-lcs (1.1.3)
|
||||
em-socksify (0.1.0)
|
||||
eventmachine
|
||||
dye (0.1.4)
|
||||
em-socksify (0.2.1)
|
||||
eventmachine (>= 1.0.0.beta.4)
|
||||
erubis (2.6.6)
|
||||
abstract (>= 1.0.0)
|
||||
excon (0.9.6)
|
||||
factory_girl (2.3.2)
|
||||
activesupport
|
||||
factory_girl_rails (1.4.0)
|
||||
factory_girl (~> 2.3.0)
|
||||
excon (0.16.10)
|
||||
factory_girl (2.6.4)
|
||||
activesupport (>= 2.3.9)
|
||||
factory_girl_rails (1.7.0)
|
||||
factory_girl (~> 2.6.0)
|
||||
railties (>= 3.0.0)
|
||||
fog (1.1.2)
|
||||
fog (1.8.0)
|
||||
builder
|
||||
excon (~> 0.9.0)
|
||||
excon (~> 0.14)
|
||||
formatador (~> 0.2.0)
|
||||
mime-types
|
||||
multi_json (~> 1.0.3)
|
||||
multi_json (~> 1.0)
|
||||
net-scp (~> 1.0.4)
|
||||
net-ssh (>= 2.1.3)
|
||||
nokogiri (~> 1.5.0)
|
||||
ruby-hmac
|
||||
formatador (0.2.1)
|
||||
formatador (0.2.4)
|
||||
globalize3 (0.3.0)
|
||||
activemodel (>= 3.0.0)
|
||||
activerecord (>= 3.0.0)
|
||||
|
@ -150,23 +160,28 @@ GEM
|
|||
mime-types (1.19)
|
||||
mini_magick (3.4)
|
||||
subexec (~> 0.2.1)
|
||||
msgpack (0.4.6)
|
||||
multi_json (1.0.4)
|
||||
mysql2 (0.2.6)
|
||||
msgpack (0.4.7)
|
||||
multi_json (1.3.7)
|
||||
mysql2 (0.2.18)
|
||||
net-scp (1.0.4)
|
||||
net-ssh (>= 1.99.1)
|
||||
net-ssh (2.3.0)
|
||||
newrelic_rpm (3.5.3.25)
|
||||
nokogiri (1.5.3)
|
||||
net-ssh (2.6.3)
|
||||
newrelic_rpm (3.5.5.38)
|
||||
nokogiri (1.5.6)
|
||||
open4 (1.3.0)
|
||||
openneo-auth-signatory (0.1.0)
|
||||
ruby-hmac
|
||||
paper_trail (2.7.0)
|
||||
activerecord (~> 3.0)
|
||||
railties (~> 3.0)
|
||||
parallel (0.5.17)
|
||||
parallel (0.5.21)
|
||||
patron (0.4.18)
|
||||
polyglot (0.3.3)
|
||||
rack (1.2.6)
|
||||
progressbar (0.11.0)
|
||||
prompter (0.1.5)
|
||||
dye (>= 0.1.1)
|
||||
yard (>= 0.6.3)
|
||||
rack (1.2.7)
|
||||
rack-fiber_pool (0.9.2)
|
||||
rack-mount (0.6.14)
|
||||
rack (>= 1.0.0)
|
||||
|
@ -190,9 +205,9 @@ GEM
|
|||
rdiscount (1.6.8)
|
||||
rdoc (3.12)
|
||||
json (~> 1.4)
|
||||
redis (2.2.2)
|
||||
redis-namespace (1.1.0)
|
||||
redis (< 3.0.0)
|
||||
redis (3.0.2)
|
||||
redis-namespace (1.2.1)
|
||||
redis (~> 3.0.0)
|
||||
resque (1.15.0)
|
||||
json (~> 1.4.6)
|
||||
redis-namespace (>= 0.10.0)
|
||||
|
@ -221,14 +236,14 @@ GEM
|
|||
rspec-rails (2.0.1)
|
||||
rspec (~> 2.0.0)
|
||||
ruby-hmac (0.4.0)
|
||||
rufus-scheduler (2.0.16)
|
||||
rufus-scheduler (2.0.17)
|
||||
tzinfo (>= 0.3.23)
|
||||
sanitize (2.0.3)
|
||||
nokogiri (>= 1.4.4, < 1.6)
|
||||
sinatra (1.2.8)
|
||||
rack (~> 1.1)
|
||||
tilt (>= 1.2.2, < 2.0)
|
||||
subexec (0.2.1)
|
||||
subexec (0.2.2)
|
||||
swf_converter (0.0.3)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.3)
|
||||
|
@ -237,14 +252,15 @@ GEM
|
|||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.35)
|
||||
uniform_notifier (1.0.2)
|
||||
vegas (0.1.8)
|
||||
vegas (0.1.11)
|
||||
rack (>= 1.0.0)
|
||||
warden (1.0.6)
|
||||
rack (>= 1.0)
|
||||
whenever (0.7.3)
|
||||
activesupport (>= 2.3.4)
|
||||
chronic (~> 0.6.3)
|
||||
will_paginate (3.0.2)
|
||||
will_paginate (3.0.4)
|
||||
yard (0.8.3)
|
||||
yui-compressor (0.9.6)
|
||||
POpen4 (>= 0.1.4)
|
||||
|
||||
|
@ -264,7 +280,8 @@ DEPENDENCIES
|
|||
em-synchrony!
|
||||
eventmachine!
|
||||
factory_girl_rails (~> 1.0)
|
||||
fog (~> 1.1.2)
|
||||
flex!
|
||||
fog (~> 1.8.0)
|
||||
globalize3
|
||||
haml (~> 3.0.18)
|
||||
hoptoad_notifier
|
||||
|
@ -280,6 +297,7 @@ DEPENDENCIES
|
|||
nokogiri (~> 1.5.2)
|
||||
openneo-auth-signatory (~> 0.1.0)
|
||||
parallel (~> 0.5.17)
|
||||
patron (~> 0.4.18)
|
||||
rack-fiber_pool
|
||||
rails (= 3.0.19)
|
||||
rdiscount (~> 1.6.5)
|
||||
|
|
|
@ -8,12 +8,11 @@ class ItemsController < ApplicationController
|
|||
per_page = params[:per_page].to_i
|
||||
per_page = 50 if per_page && per_page > 50
|
||||
else
|
||||
per_page = nil
|
||||
per_page = 30
|
||||
end
|
||||
# Note that we sort by name by hand, since we might have to use
|
||||
# fallbacks after the fact
|
||||
@items = Item.search(@query, current_user, I18n.default_locale).
|
||||
alphabetize_by_translations.
|
||||
@items = Item::Search::Query.from_text(@query, current_user).
|
||||
paginate(:page => params[:page], :per_page => per_page)
|
||||
assign_closeted!
|
||||
respond_to do |format|
|
||||
|
|
18
app/flex/flex_search.rb
Normal file
18
app/flex/flex_search.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# inspect the methods loaded in this module and their usage
|
||||
# in the rails console by just typing:
|
||||
# >> puts FlexSearch.flex.info
|
||||
# you can eventually restrict the info to a single method by pasing its name:
|
||||
# >> puts FlexSearch.flex.info :search
|
||||
# see the detailed doc for this feature at https://github.com/ddnexus/flex/wiki/Selfdoc
|
||||
|
||||
module FlexSearch
|
||||
|
||||
extend self
|
||||
|
||||
include Flex::Loader
|
||||
flex.load_search_source
|
||||
|
||||
# you may need to add more method here, usually wrapper methods
|
||||
# that use one of the autogenerated methods from the loaded templates
|
||||
|
||||
end
|
45
app/flex/flex_search.yml
Normal file
45
app/flex/flex_search.yml
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Add here your search queries
|
||||
# see the detailed Source documentation at https://github.com/ddnexus/flex/wiki/Sources
|
||||
|
||||
|
||||
# ANCHORS litheral key: it will not be used as template
|
||||
# you can store here fragments of queries to reuse in the templates below
|
||||
ANCHORS:
|
||||
- &name_locale_partial
|
||||
"name.<<locale>>^<<boost= 1>>"
|
||||
|
||||
_name_locales:
|
||||
*name_locale_partial
|
||||
|
||||
_negative_name_locales:
|
||||
*name_locale_partial
|
||||
|
||||
item_search:
|
||||
- query:
|
||||
bool:
|
||||
must:
|
||||
- multi_match:
|
||||
query: <<name= ~>>
|
||||
fields: <<_name_locales>>
|
||||
- term:
|
||||
is_nc: <<is_nc= ~>>
|
||||
- term:
|
||||
is_pb: <<is_pb= ~>>
|
||||
- term:
|
||||
species_support_id: <<species_support_id= ~>>
|
||||
- term:
|
||||
occupied_zone_id: <<occupied_zone_id= ~>>
|
||||
- term:
|
||||
restricted_zone_id: <<restricted_zone_id= ~>>
|
||||
must_not:
|
||||
- term:
|
||||
species_support_id: <<negative_species_support_id= ~>>
|
||||
- term:
|
||||
occupied_zone_id: <<negative_occupied_zone_id= ~>>
|
||||
- term:
|
||||
restricted_zone_id: <<negative_restricted_zone_id= ~>>
|
||||
- multi_match:
|
||||
query: <<negative_name= ~>>
|
||||
fields: <<_negative_name_locales>>
|
||||
sort:
|
||||
- name.<<locale>>.untouched
|
36
app/flex/flex_search_extender.rb
Normal file
36
app/flex/flex_search_extender.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# see the detailed Extenders documentation at https://github.com/ddnexus/flex/wiki/Extenders
|
||||
|
||||
module FlexSearchExtender
|
||||
|
||||
# set this method to restrict this extender to certain types of results
|
||||
# see the other Flex extenders for reference (https://github.com/ddnexus/flex/tree/master/lib/flex/result)
|
||||
def self.should_extend?(response)
|
||||
true
|
||||
end
|
||||
|
||||
def scoped_loaded_collection(options)
|
||||
options[:scopes] ||= {}
|
||||
@loaded_collection ||= begin
|
||||
records = []
|
||||
# returns a structure like {Comment=>[{"_id"=>"123", ...}, {...}], BlogPost=>[...]}
|
||||
h = Flex::Utils.group_array_by(collection) do |d|
|
||||
d.mapped_class(should_raise=true)
|
||||
end
|
||||
h.each do |klass, docs|
|
||||
scope = options[:scopes][klass.name] || klass.scoped
|
||||
records |= scope.find(docs.map(&:_id))
|
||||
end
|
||||
class_ids = collection.map { |d| [d.mapped_class.to_s, d._id] }
|
||||
# Reorder records to preserve order from search results
|
||||
records = class_ids.map do |class_str, id|
|
||||
records.detect do |record|
|
||||
record.class.to_s == class_str && record.id.to_s == id.to_s
|
||||
end
|
||||
end
|
||||
records.extend Flex::Result::Collection
|
||||
records.setup(self['hits']['total'], variables)
|
||||
records
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
class Item < ActiveRecord::Base
|
||||
include Flex::Model
|
||||
include PrettyParam
|
||||
|
||||
set_inheritance_column 'inheritance_type' # PHP Impress used "type" to describe category
|
||||
|
@ -45,6 +46,27 @@ class Item < ActiveRecord::Base
|
|||
|
||||
scope :with_closet_hangers, joins(:closet_hangers)
|
||||
|
||||
flex.sync self
|
||||
|
||||
def flex_source
|
||||
indexed_attributes = {
|
||||
:is_nc => self.nc?,
|
||||
:is_pb => self.pb?,
|
||||
:species_support_id => self.species_support_ids,
|
||||
:occupied_zone_id => self.occupied_zone_ids,
|
||||
:restricted_zone_id => self.restricted_zone_ids,
|
||||
:name => {}
|
||||
}
|
||||
|
||||
I18n.usable_locales_with_neopets_language_code.each do |locale|
|
||||
Globalize.with_locale(locale) do
|
||||
indexed_attributes[:name][locale] = self.name
|
||||
end
|
||||
end
|
||||
|
||||
indexed_attributes.to_json
|
||||
end
|
||||
|
||||
def closeted?
|
||||
@owned || @wanted
|
||||
end
|
||||
|
@ -79,6 +101,10 @@ class Item < ActiveRecord::Base
|
|||
NCRarities.include?(rarity_index)
|
||||
end
|
||||
|
||||
def pb?
|
||||
(self.description == PAINTBRUSH_SET_DESCRIPTION)
|
||||
end
|
||||
|
||||
def owned?
|
||||
@owned
|
||||
end
|
||||
|
@ -97,6 +123,14 @@ class Item < ActiveRecord::Base
|
|||
@restricted_zones
|
||||
end
|
||||
|
||||
def restricted_zone_ids
|
||||
restricted_zones.map(&:id)
|
||||
end
|
||||
|
||||
def occupied_zone_ids
|
||||
occupied_zones.map(&:id)
|
||||
end
|
||||
|
||||
def occupied_zones
|
||||
all_body_ids = []
|
||||
zone_body_ids = {}
|
||||
|
|
8
app/models/item/search.rb
Normal file
8
app/models/item/search.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
class Item
|
||||
module Search
|
||||
def self.error(key, *args)
|
||||
message = I18n.translate("items.search.errors.#{key}", *args)
|
||||
raise Item::Search::Error, message
|
||||
end
|
||||
end
|
||||
end
|
7
app/models/item/search/contradiction.rb
Normal file
7
app/models/item/search/contradiction.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class Item
|
||||
module Search
|
||||
class Contradiction < Error
|
||||
|
||||
end
|
||||
end
|
||||
end
|
7
app/models/item/search/error.rb
Normal file
7
app/models/item/search/error.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class Item
|
||||
module Search
|
||||
class Error < RuntimeError
|
||||
|
||||
end
|
||||
end
|
||||
end
|
11
app/models/item/search/field.rb
Normal file
11
app/models/item/search/field.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Item
|
||||
module Search
|
||||
class Field
|
||||
attr_reader :key
|
||||
|
||||
def initialize(key)
|
||||
@key = key
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
20
app/models/item/search/fields/flag.rb
Normal file
20
app/models/item/search/fields/flag.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
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
|
44
app/models/item/search/fields/set_field.rb
Normal file
44
app/models/item/search/fields/set_field.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
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 filter.value.respond_to?(:each)
|
||||
filter.value.each do |value|
|
||||
add_value(value, filter.positive?)
|
||||
end
|
||||
else
|
||||
add_value(filter.value, filter.positive?)
|
||||
end
|
||||
end
|
||||
|
||||
def to_flex_params
|
||||
{
|
||||
key => nil_if_empty(@values[true]),
|
||||
:"negative_#{key}" => nil_if_empty(@values[false])
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_value(value, is_positive)
|
||||
if @values[!is_positive].include?(value)
|
||||
raise Item::Search::Contradiction,
|
||||
"positive #{key} and negative #{key} both contain #{value}"
|
||||
end
|
||||
|
||||
@values[is_positive] << value
|
||||
end
|
||||
|
||||
def nil_if_empty(set)
|
||||
set unless set.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
29
app/models/item/search/fields/text_field.rb
Normal file
29
app/models/item/search/fields/text_field.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
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
|
17
app/models/item/search/filter.rb
Normal file
17
app/models/item/search/filter.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
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
|
||||
end
|
||||
end
|
||||
end
|
151
app/models/item/search/query.rb
Normal file
151
app/models/item/search/query.rb
Normal file
|
@ -0,0 +1,151 @@
|
|||
class Item
|
||||
module Search
|
||||
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::TextField
|
||||
}
|
||||
|
||||
def initialize(filters, user)
|
||||
@filters = filters
|
||||
end
|
||||
|
||||
def fields
|
||||
initial_fields.tap do |fields|
|
||||
@filters.each { |filter| fields[filter.key] << filter }
|
||||
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 []
|
||||
end
|
||||
|
||||
final_flex_params = {
|
||||
:page => (options[:page] || 1),
|
||||
:size => (options[:per_page] || 30)
|
||||
}.merge(flex_params)
|
||||
|
||||
locales = I18n.fallbacks[I18n.locale] &
|
||||
I18n.locales_with_neopets_language_code
|
||||
final_flex_params[:locale] = locales.first
|
||||
|
||||
if final_flex_params[:name] || final_flex_params[:negative_name]
|
||||
locale_entries = locales.map do |locale|
|
||||
boost = (locale == I18n.locale) ? 4 : 1
|
||||
{:locale => locale, :boost => boost}
|
||||
end
|
||||
|
||||
if final_flex_params[:name]
|
||||
final_flex_params[:_name_locales] = locale_entries
|
||||
end
|
||||
|
||||
if final_flex_params[:negative_name]
|
||||
final_flex_params[:_negative_name_locales] = locale_entries
|
||||
end
|
||||
end
|
||||
|
||||
result = FlexSearch.item_search(final_flex_params)
|
||||
result.scoped_loaded_collection(:scopes => {'Item' => Item.with_translations})
|
||||
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 = {}
|
||||
I18n.available_locales.each do |locale|
|
||||
TEXT_KEYS_BY_LABEL[locale] = {}
|
||||
FIELD_CLASSES.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 => locale).split(',')
|
||||
labels.each { |label| TEXT_KEYS_BY_LABEL[locale][label] = key }
|
||||
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 { |name|
|
||||
zone_set = Zone.find_set(name)
|
||||
unless zone_set
|
||||
Item::Search.error 'not_found.zone', :zone_name => name
|
||||
end
|
||||
zone_set.map(&:id)
|
||||
}
|
||||
}
|
||||
|
||||
TEXT_QUERY_RESOURCE_TYPES_BY_KEY = {
|
||||
:species_support_id => :species,
|
||||
:occupied_zone_id => :zone,
|
||||
:restricted_zone_id => :zone
|
||||
}
|
||||
|
||||
TEXT_FILTER_EXPR = /([+-]?)(?:([a-z]+):)?(?:"([^"]+)"|(\S+))/
|
||||
def self.from_text(text, user=nil)
|
||||
filters = []
|
||||
text.scan(TEXT_FILTER_EXPR) do |sign, label, quoted_value, unquoted_value|
|
||||
label ||= 'name'
|
||||
raw_value = quoted_value || unquoted_value
|
||||
is_positive = (sign != '-')
|
||||
|
||||
if label == 'is'
|
||||
# is-filters are weird. "-is:nc" is transposed to something more
|
||||
# like "-nc:<nil>", then it's translated into a negative "is_nc"
|
||||
# flag.
|
||||
label = raw_value
|
||||
raw_value = nil
|
||||
end
|
||||
|
||||
key = TEXT_KEYS_BY_LABEL[I18n.locale][label]
|
||||
if key.nil?
|
||||
message = I18n.translate('items.search.errors.not_found.label',
|
||||
:label => label)
|
||||
raise Item::Search::Error, message
|
||||
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
|
||||
|
||||
self.new(filters, user)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# The fields start out empty, then have the filters inserted into 'em,
|
||||
# so that the fields can validate and aggregate their requirements.
|
||||
def initial_fields
|
||||
{}.tap do |fields|
|
||||
FIELD_CLASSES.map do |key, klass|
|
||||
fields[key] = klass.new(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
config/flex.yml
Normal file
36
config/flex.yml
Normal file
|
@ -0,0 +1,36 @@
|
|||
# ANCHORS litheral key: it will not be used as template
|
||||
# you can store here fragments of structures to reuse below
|
||||
ANCHORS:
|
||||
-
|
||||
|
||||
# This is a dynamic index name The settings and mapping below will work with any index.
|
||||
# The default index name generated by Flex is usually <application_name>_<environment>,
|
||||
# but you may have changed it in the initializers/flex.rb or you can hardcode it if you prefer.
|
||||
<%= Flex::Configuration.variables[:index] %>:
|
||||
|
||||
settings:
|
||||
number_of_shards: 5
|
||||
number_of_replicas: 1
|
||||
|
||||
# add your custom mappings here
|
||||
mappings:
|
||||
item:
|
||||
properties:
|
||||
# Name is an object of locale fields, which are in turn multi_fields.
|
||||
# First, an analyzed string for searching. Second, an untouched
|
||||
# string for sorting. Elasticsearch requires that both be expliticly
|
||||
# named in the mapping, but will handle the copy implicitly.
|
||||
name:
|
||||
type: object
|
||||
properties:
|
||||
<% I18n.usable_locales_with_neopets_language_code.each do |locale| %>
|
||||
<%= locale %>:
|
||||
type: multi_field
|
||||
fields:
|
||||
<%= locale %>:
|
||||
type: string
|
||||
index: analyzed
|
||||
untouched:
|
||||
type: string
|
||||
index: not_analyzed
|
||||
<% end %>
|
44
config/initializers/flex.rb
Normal file
44
config/initializers/flex.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# see the detailed Configuration documentation at https://github.com/ddnexus/flex/wiki/Configuration
|
||||
|
||||
Flex::Configuration.configure do |config|
|
||||
|
||||
# you MUST add your indexed model names here
|
||||
config.flex_models = %w[ Item ]
|
||||
|
||||
# Add the your result extenders here
|
||||
config.result_extenders |= [ FlexSearchExtender ]
|
||||
|
||||
# Add the default variables here
|
||||
# see also the details Variables documentation at https://github.com/ddnexus/flex/wiki/Variables
|
||||
# config.variables.add :index => 'my_index',
|
||||
# :type => 'project',
|
||||
# :anything => 'anything
|
||||
|
||||
# The custom url of your ElasticSearch server
|
||||
# config.base_uri = 'http://localhost:9200'
|
||||
|
||||
# Set it to true to log the debug infos (true by default in development mode)
|
||||
# config.debug = false
|
||||
|
||||
# Debug info are actually valid curl commands
|
||||
# config.debug_to_curl = false
|
||||
|
||||
# The custom logger you want Flex to use. Default Rails.logger
|
||||
# config.logger = Logger.new(STDERR)
|
||||
|
||||
# Custom config file path
|
||||
# config.config_file = '/custom/path/to/flex.yml',
|
||||
|
||||
# Custom flex dir path
|
||||
# config.flex_dir = '/custom/path/to/flex',
|
||||
|
||||
# The custom http_client you may want to implement
|
||||
# config.http_client = 'Your::Client'
|
||||
|
||||
# The options passed to the http_client. They are client specific.
|
||||
# config.http_client_options = {:timeout => 5}
|
||||
|
||||
# Experimental: checks the response and return a boolean (should raise?)
|
||||
# config.raise_proc = proc{|response| response.status >= 400}
|
||||
|
||||
end
|
|
@ -32,6 +32,9 @@ config.each do |locale_str, locale_meta|
|
|||
end
|
||||
end
|
||||
|
||||
LocaleMeta::USABLE_LOCALES_WITH_NEOPETS_LANGUAGE_CODE = LocaleMeta::USABLE_LOCALES &
|
||||
LocaleMeta::LOCALES_WITH_NEOPETS_LANGUAGE_CODE
|
||||
|
||||
module I18n
|
||||
def self.public_locales
|
||||
LocaleMeta::PUBLIC_LOCALES
|
||||
|
@ -45,6 +48,10 @@ module I18n
|
|||
LocaleMeta::LOCALES_WITH_NEOPETS_LANGUAGE_CODE
|
||||
end
|
||||
|
||||
def self.usable_locales_with_neopets_language_code
|
||||
LocaleMeta::USABLE_LOCALES_WITH_NEOPETS_LANGUAGE_CODE
|
||||
end
|
||||
|
||||
def self.neopets_language_code_for(locale)
|
||||
LocaleMeta::NEOPETS_LANGUAGE_CODES_BY_LOCALE[locale]
|
||||
end
|
||||
|
|
|
@ -326,6 +326,21 @@ en:
|
|||
header: Brought to you by
|
||||
footer: Thanks!
|
||||
|
||||
search:
|
||||
errors:
|
||||
not_found:
|
||||
label: Filter "%{label}" does not exist. Is it spelled correctly?
|
||||
species:
|
||||
Species "%{species_name}" does not exist. Is it spelled correctly?
|
||||
zone: Zone "%{zone_name}" does not exist. Is it spelled correctly?
|
||||
labels:
|
||||
name: name
|
||||
is_nc: nc
|
||||
is_pb: pb
|
||||
species_support_id: species
|
||||
occupied_zone_id: occupies,zone,type
|
||||
restricted_zone_id: restricts
|
||||
|
||||
neopets_pages:
|
||||
create:
|
||||
success: Page %{index} saved!
|
||||
|
|
BIN
vendor/cache/addressable-2.2.6.gem
vendored
BIN
vendor/cache/addressable-2.2.6.gem
vendored
Binary file not shown.
BIN
vendor/cache/addressable-2.3.2.gem
vendored
Normal file
BIN
vendor/cache/addressable-2.3.2.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/bullet-4.1.5.gem
vendored
BIN
vendor/cache/bullet-4.1.5.gem
vendored
Binary file not shown.
BIN
vendor/cache/bullet-4.1.6.gem
vendored
Normal file
BIN
vendor/cache/bullet-4.1.6.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/closure-compiler-1.1.4.gem
vendored
BIN
vendor/cache/closure-compiler-1.1.4.gem
vendored
Binary file not shown.
BIN
vendor/cache/closure-compiler-1.1.8.gem
vendored
Normal file
BIN
vendor/cache/closure-compiler-1.1.8.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/dye-0.1.4.gem
vendored
Normal file
BIN
vendor/cache/dye-0.1.4.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/em-socksify-0.1.0.gem
vendored
BIN
vendor/cache/em-socksify-0.1.0.gem
vendored
Binary file not shown.
BIN
vendor/cache/em-socksify-0.2.1.gem
vendored
Normal file
BIN
vendor/cache/em-socksify-0.2.1.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/excon-0.16.10.gem
vendored
Normal file
BIN
vendor/cache/excon-0.16.10.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/excon-0.9.6.gem
vendored
BIN
vendor/cache/excon-0.9.6.gem
vendored
Binary file not shown.
BIN
vendor/cache/factory_girl-2.3.2.gem
vendored
BIN
vendor/cache/factory_girl-2.3.2.gem
vendored
Binary file not shown.
BIN
vendor/cache/factory_girl-2.6.4.gem
vendored
Normal file
BIN
vendor/cache/factory_girl-2.6.4.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/factory_girl_rails-1.4.0.gem
vendored
BIN
vendor/cache/factory_girl_rails-1.4.0.gem
vendored
Binary file not shown.
BIN
vendor/cache/factory_girl_rails-1.7.0.gem
vendored
Normal file
BIN
vendor/cache/factory_girl_rails-1.7.0.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/fog-1.1.2.gem
vendored
BIN
vendor/cache/fog-1.1.2.gem
vendored
Binary file not shown.
BIN
vendor/cache/fog-1.8.0.gem
vendored
Normal file
BIN
vendor/cache/fog-1.8.0.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/formatador-0.2.1.gem
vendored
BIN
vendor/cache/formatador-0.2.1.gem
vendored
Binary file not shown.
BIN
vendor/cache/formatador-0.2.4.gem
vendored
Normal file
BIN
vendor/cache/formatador-0.2.4.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/msgpack-0.4.6.gem
vendored
BIN
vendor/cache/msgpack-0.4.6.gem
vendored
Binary file not shown.
BIN
vendor/cache/msgpack-0.4.7.gem
vendored
Normal file
BIN
vendor/cache/msgpack-0.4.7.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/multi_json-1.0.4.gem
vendored
BIN
vendor/cache/multi_json-1.0.4.gem
vendored
Binary file not shown.
BIN
vendor/cache/multi_json-1.3.7.gem
vendored
Normal file
BIN
vendor/cache/multi_json-1.3.7.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/mysql2-0.2.18.gem
vendored
Normal file
BIN
vendor/cache/mysql2-0.2.18.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/mysql2-0.2.6.gem
vendored
BIN
vendor/cache/mysql2-0.2.6.gem
vendored
Binary file not shown.
BIN
vendor/cache/net-ssh-2.3.0.gem
vendored
BIN
vendor/cache/net-ssh-2.3.0.gem
vendored
Binary file not shown.
BIN
vendor/cache/net-ssh-2.6.3.gem
vendored
Normal file
BIN
vendor/cache/net-ssh-2.6.3.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/newrelic_rpm-3.5.3.25.gem
vendored
BIN
vendor/cache/newrelic_rpm-3.5.3.25.gem
vendored
Binary file not shown.
BIN
vendor/cache/newrelic_rpm-3.5.5.38.gem
vendored
Normal file
BIN
vendor/cache/newrelic_rpm-3.5.5.38.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/nokogiri-1.5.3.gem
vendored
BIN
vendor/cache/nokogiri-1.5.3.gem
vendored
Binary file not shown.
BIN
vendor/cache/nokogiri-1.5.6.gem
vendored
Normal file
BIN
vendor/cache/nokogiri-1.5.6.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/parallel-0.5.17.gem
vendored
BIN
vendor/cache/parallel-0.5.17.gem
vendored
Binary file not shown.
BIN
vendor/cache/parallel-0.5.21.gem
vendored
Normal file
BIN
vendor/cache/parallel-0.5.21.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/patron-0.4.18.gem
vendored
Normal file
BIN
vendor/cache/patron-0.4.18.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/progressbar-0.11.0.gem
vendored
Normal file
BIN
vendor/cache/progressbar-0.11.0.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/prompter-0.1.5.gem
vendored
Normal file
BIN
vendor/cache/prompter-0.1.5.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/rack-1.2.6.gem
vendored
BIN
vendor/cache/rack-1.2.6.gem
vendored
Binary file not shown.
BIN
vendor/cache/rack-1.2.7.gem
vendored
Normal file
BIN
vendor/cache/rack-1.2.7.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/redis-2.2.2.gem
vendored
BIN
vendor/cache/redis-2.2.2.gem
vendored
Binary file not shown.
BIN
vendor/cache/redis-3.0.2.gem
vendored
Normal file
BIN
vendor/cache/redis-3.0.2.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/redis-namespace-1.1.0.gem
vendored
BIN
vendor/cache/redis-namespace-1.1.0.gem
vendored
Binary file not shown.
BIN
vendor/cache/redis-namespace-1.2.1.gem
vendored
Normal file
BIN
vendor/cache/redis-namespace-1.2.1.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/rufus-scheduler-2.0.16.gem
vendored
BIN
vendor/cache/rufus-scheduler-2.0.16.gem
vendored
Binary file not shown.
BIN
vendor/cache/rufus-scheduler-2.0.17.gem
vendored
Normal file
BIN
vendor/cache/rufus-scheduler-2.0.17.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/subexec-0.2.1.gem
vendored
BIN
vendor/cache/subexec-0.2.1.gem
vendored
Binary file not shown.
BIN
vendor/cache/subexec-0.2.2.gem
vendored
Normal file
BIN
vendor/cache/subexec-0.2.2.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/vegas-0.1.11.gem
vendored
Normal file
BIN
vendor/cache/vegas-0.1.11.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/vegas-0.1.8.gem
vendored
BIN
vendor/cache/vegas-0.1.8.gem
vendored
Binary file not shown.
BIN
vendor/cache/will_paginate-3.0.2.gem
vendored
BIN
vendor/cache/will_paginate-3.0.2.gem
vendored
Binary file not shown.
BIN
vendor/cache/will_paginate-3.0.4.gem
vendored
Normal file
BIN
vendor/cache/will_paginate-3.0.4.gem
vendored
Normal file
Binary file not shown.
BIN
vendor/cache/yard-0.8.3.gem
vendored
Normal file
BIN
vendor/cache/yard-0.8.3.gem
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue