Permalink
Browse files

Merge branch 'merge_shopify'

  • Loading branch information...
2 parents d2cef2a + 7cccafa commit 3b8275dd6b4c52bfe9909c4d1db6c86213d2a4a3 @mattgleeson committed Aug 10, 2011
View
8 README
@@ -6,7 +6,7 @@ Ruby on Rails configurator and client to the Sphinx full text search engine.
Copyright 2007-2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. Some portions copyright Pat Allan, distributed under the MIT license, and used with permission. Some portions copyright PJ Hyett and Mislav Marohnić, distributed under the MIT license, and used with permission.
-The public certificate for the gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem].
+The public certificate for the gem is here[http://blog.evanweaver.com/files/evan_weaver-original-public_cert.pem].
If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
@@ -52,9 +52,9 @@ You also need the <tt>chronic</tt> gem:
Then, install the plugin:
script/plugin install git://github.com/fauna/ultrasphinx.git
-Next, copy the <tt>examples/default.base</tt> file to <tt>RAILS_ROOT/config/ultrasphinx/default.base</tt>. This file sets up the Sphinx daemon options such as port, host, and index location.
+Next, copy the <tt>examples/default.base</tt> file to <tt>Rails.root/config/ultrasphinx/default.base</tt>. This file sets up the Sphinx daemon options such as port, host, and index location.
-If you need per-environment configuration, you can use <tt>RAILS_ROOT/config/ultrasphinx/development.base</tt>, etc. Note that ERb is also allowed within the <tt>.base</tt> files, and can be an alternative way to DRY up multiple configurations.
+If you need per-environment configuration, you can use <tt>Rails.root/config/ultrasphinx/development.base</tt>, etc. Note that ERb is also allowed within the <tt>.base</tt> files, and can be an alternative way to DRY up multiple configurations.
Now, in your models, use the <tt>is_indexed</tt> method to configure a model as searchable. For example:
@@ -128,7 +128,7 @@ PostgreSQL 8.2 and higher are well supported. However, make sure the stored proc
== Reporting problems
-The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=14244].
+The support forum is here[http://github.com/fauna/ultrasphinx/issues].
Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
@@ -1,8 +1,8 @@
class InstallUltrasphinxStoredProcedures < ActiveRecord::Migration
- def self.up
- Dir.chdir("#{RAILS_ROOT}/vendor/plugins/ultrasphinx/lib/ultrasphinx/postgresql") do
- # Create the plpgsql language
+ def self.up
+ Dir.chdir("#{Rails.root}/vendor/plugins/ultrasphinx/lib/ultrasphinx/postgresql") do
+ # Create the plpgsql language
execute "CREATE LANGUAGE plpgsql" rescue nil
# Create the rest of the functions
Dir["*.sql"].each do |filename|
View
@@ -3,7 +3,7 @@
require 'chronic'
require 'singleton'
-if defined? RAILS_ENV and RAILS_ENV == "development"
+if defined? Rails.env and Rails.env.development?
if ENV['USER'] == 'eweaver'
require 'ruby-debug'
Debugger.start
@@ -33,4 +33,3 @@
require 'ultrasphinx/spell'
end
-
@@ -8,7 +8,7 @@ class << self
# Force all the indexed models to load and register in the MODEL_CONFIGURATION hash.
def load_constants
- Dir.chdir "#{RAILS_ROOT}/app/models/" do
+ Dir.chdir "#{Rails.root}/app/models/" do
Dir["**/*.rb"].each do |filename|
open(filename) do |file|
begin
@@ -22,7 +22,7 @@ def load_constants
end
rescue Object => e
say "warning: critical autoload error on #{filename}; try referencing \"#{filename.camelize}\" directly in the console"
- say e.backtrace.join("\n") if RAILS_ENV == "development"
+ say e.backtrace.join("\n") if Rails.env.development?
end
end
end
@@ -38,7 +38,7 @@ def run
load_constants
- say "rebuilding configurations for #{RAILS_ENV} environment"
+ say "rebuilding configurations for #{Rails.env} environment"
# stable sort classes by name rather than rely on hash order
model_list = MODEL_CONFIGURATION.keys.sort
say "available models are #{model_list.to_sentence}"
@@ -310,6 +310,10 @@ def build_concatenations(klass, fields, entries, column_strings, join_strings, g
source_string = "#{entry['table_alias']}.#{entry['field']}"
order_string = ("ORDER BY #{entry['order']}" if entry['order'])
# We are using the field in an aggregate, so we don't want to add it to group_bys
+ if entry['multi']
+ source_string = SQL_FUNCTIONS[ADAPTER]['hash']._interpolate(source_string)
+ end
+
source_string = SQL_FUNCTIONS[ADAPTER]['group_concat']._interpolate(source_string, order_string)
use_distinct = true
@@ -326,7 +330,7 @@ def build_concatenations(klass, fields, entries, column_strings, join_strings, g
column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], entry['sortable'], column_strings, remaining_columns)
else
- raise ConfigurationError, "Invalid concatenate parameters for #{model}: #{entry.inspect}."
+ raise ConfigurationError, "Invalid concatenate parameters for #{klass}: #{entry.inspect}."
end
end
View
@@ -13,6 +13,7 @@ class Fields
TYPE_MAP = {
'string' => 'text',
+ 'multi' => 'multi',
'text' => 'text',
'integer' => 'integer',
'date' => 'date',
@@ -57,13 +58,15 @@ def save_and_verify_type(field, new_type, string_sortable, klass, msg = nil)
@groups << case new_type
when 'integer'
"sql_attr_uint = #{field}"
+ when 'multi'
+ "sql_attr_multi = uint #{field} from field"
when 'float'
"sql_attr_float = #{field}"
when 'bool'
"sql_attr_bool = #{field}"
when 'date'
"sql_attr_timestamp = #{field}"
- when 'text'
+ when 'text'
"sql_attr_str2ordinal = #{field}" if string_sortable
end
end
@@ -85,7 +88,7 @@ def null(field)
case types[field]
when 'text'
"''"
- when 'integer', 'float', 'bool'
+ when 'integer', 'float', 'bool', 'multi'
"0"
when 'date'
"18000" # Midnight on 1/1/1970
@@ -135,7 +138,11 @@ def configure(configuration)
# Regular concats are CHAR, group_concats are BLOB and need to be cast to CHAR
options['concatenate'].to_a.each do |entry|
extract_table_alias!(entry, klass)
- save_and_verify_type(entry['as'], 'text', nil, klass)
+ if entry['multi']
+ save_and_verify_type(entry['as'], 'multi', nil, klass)
+ else
+ save_and_verify_type(entry['as'], 'text', nil, klass)
+ end
install_duplicate_fields!(entry, klass)
end
@@ -197,7 +197,7 @@ def self.is_indexed opts = {}
opts['fields'].map! do |entry|
if entry.is_a? Hash
entry._stringify_all!('sortable', 'facet')
- entry.assert_valid_keys ['field', 'as', 'facet', 'function_sql', 'sortable', 'table_alias', 'type']
+ entry.assert_valid_keys ['field', 'as', 'facet', 'function_sql', 'sortable', 'table_alias', 'type', "multi"]
entry
else
# Single strings
@@ -208,7 +208,7 @@ def self.is_indexed opts = {}
opts['concatenate'].each do |entry|
entry._stringify_all!('fields', 'sortable', 'facet')
- entry.assert_valid_keys ['class_name', 'association_name', 'conditions', 'field', 'as', 'fields', 'association_sql', 'facet', 'function_sql', 'sortable', 'order', 'table_alias']
+ entry.assert_valid_keys ['class_name', 'association_name', 'conditions', 'field', 'as', 'fields', 'association_sql', 'facet', 'function_sql', 'sortable', 'order', 'table_alias', "multi"]
raise Ultrasphinx::ConfigurationError, "You can't mix regular concat and group concats" if entry['fields'] and (entry['field'] or entry['class_name'] or entry['association_name'])
raise Ultrasphinx::ConfigurationError, "Concatenations must specify an :as key" unless entry['as']
raise Ultrasphinx::ConfigurationError, "Group concatenations must not have multiple fields" if entry['field'].is_a? Array
@@ -320,14 +320,14 @@ def initialize opts = {}
@options['indexes'] = Array(@options['indexes']).join(" ")
raise UsageError, "Weights must be a Hash" unless @options['weights'].is_a? Hash
- raise UsageError, "Filters must be a Hash" unless @options['filters'].is_a? Hash
+ # raise UsageError, "Filters must be a Hash" unless @options['filters'].is_a? Hash
@options['parsed_query'] = parse(query)
@results, @subtotals, @facets, @response = [], {}, {}, {}
extra_keys = @options.keys - (self.class.query_defaults.keys + INTERNAL_KEYS)
- log "discarded invalid keys: #{extra_keys * ', '}" if extra_keys.any? and RAILS_ENV != "test"
+ log "discarded invalid keys: #{extra_keys * ', '}" if extra_keys.any? and !Rails.env.test?
end
# Run the search, filling results with an array of ActiveRecord objects. Set the parameter to false
@@ -105,24 +105,43 @@ def build_request_with_options opts
raise UsageError, "field #{field.inspect} is invalid" unless type
+ exclude = false
+
+ # check for exclude flag attached to filter
+ if value.is_a?(Hash)
+ exclude = value[:exclude]
+ value = value[:value]
+ end
+
begin
case value
when Integer, Float, BigDecimal, NilClass, Array
# XXX Hack to force floats to be floats
value = value.to_f if type == 'float'
# Just bomb the filter in there
- request.filters << Riddle::Client::Filter.new(field, Array(value), false)
+
+ if type == 'multi'
+ # hack to force crc32 conversion on multi-value attributes
+ value.map! { |v| Zlib.crc32(v) }
+ end
+ request.filters << Riddle::Client::Filter.new(field, Array(value), exclude)
when Range
# Make sure ranges point in the right direction
min, max = [value.begin, value.end].map {|x| x._to_numeric }
raise NoMethodError unless min <=> max and max <=> min
min, max = max, min if min > max
# XXX Hack to force floats to be floats
min, max = min.to_f, max.to_f if type == 'float'
- request.filters << Riddle::Client::Filter.new(field, min..max, false)
+ request.filters << Riddle::Client::Filter.new(field, min..max, exclude)
when String
- # XXX Hack to move text filters into the query
- opts['parsed_query'] << " @#{field} #{value}"
+ if type == 'multi'
+ # hack to force crc32 conversion on multi-value attributes
+ value = Zlib.crc32(value)
+ request.filters << Riddle::Client::Filter.new(field, Array(value), exclude)
+ else
+ # XXX Hack to move text filters into the query
+ opts['parsed_query'] << " @#{field} #{value}"
+ end
else
raise NoMethodError
end
@@ -270,7 +289,7 @@ def rebuild_facet_cache(facet)
def convert_sphinx_ids(sphinx_ids)
number_of_models = IDS_TO_MODELS.size
- raise ConfigurationError, "No model mappings were found. Your #{RAILS_ENV}.conf file is corrupted, or your application container needs to be restarted." if number_of_models == 0
+ raise ConfigurationError, "No model mappings were found. Your #{Rails.env}.conf file is corrupted, or your application container needs to be restarted." if number_of_models == 0
sphinx_ids.sort_by do |item|
item[:index]
@@ -14,19 +14,19 @@ class UsageError < Error #:nodoc:
SUBDIR = "config/ultrasphinx"
- DIR = "#{RAILS_ROOT}/#{SUBDIR}"
+ DIR = "#{Rails.root}/#{SUBDIR}"
THIS_DIR = File.expand_path(File.dirname(__FILE__))
- CONF_PATH = "#{DIR}/#{RAILS_ENV}.conf"
+ CONF_PATH = "#{DIR}/#{Rails.env}.conf"
- ENV_BASE_PATH = "#{DIR}/#{RAILS_ENV}.base"
+ ENV_BASE_PATH = "#{DIR}/#{Rails.env}.base"
GENERIC_BASE_PATH = "#{DIR}/default.base"
BASE_PATH = (File.exist?(ENV_BASE_PATH) ? ENV_BASE_PATH : GENERIC_BASE_PATH)
- raise ConfigurationError, "Please create a '#{SUBDIR}/#{RAILS_ENV}.base' or '#{SUBDIR}/default.base' file in order to use Ultrasphinx in your #{RAILS_ENV} environment." unless File.exist? BASE_PATH # XXX lame
+ raise ConfigurationError, "Please create a '#{SUBDIR}/#{Rails.env}.base' or '#{SUBDIR}/default.base' file in order to use Ultrasphinx in your #{Rails.env} environment." unless File.exist? BASE_PATH # XXX lame
# Some miscellaneous constants
@@ -64,7 +64,7 @@ def self.load_stored_procedure(name)
SQL_FUNCTIONS = {
'mysql' => {
- 'group_concat' => "CAST(GROUP_CONCAT(DISTINCT ? ? SEPARATOR ' ') AS CHAR)",
+ 'group_concat' => "GROUP_CONCAT(DISTINCT ? ? SEPARATOR ' ')",
'delta' => "DATE_SUB(NOW(), INTERVAL ? SECOND)",
'hash' => "CAST(CRC32(?) AS unsigned)",
'range_cast' => "?"
@@ -100,8 +100,8 @@ def self.say msg
puts msg[0..0].upcase + msg[1..-1]
else
msg = "** ultrasphinx: #{msg}"
- if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER
- RAILS_DEFAULT_LOGGER.warn msg
+ if defined?(Rails) && Rails.logger
+ Rails.logger.warn msg
else
STDERR.puts msg
end
@@ -112,8 +112,8 @@ def self.say msg
# Debug-mode logger.
def self.log msg
# XXX Method name is stupid.
- if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER
- RAILS_DEFAULT_LOGGER.debug msg
+ if defined?(Rails) && Rails.logger
+ Rails.logger.debug msg
else
STDERR.puts msg
end
@@ -133,7 +133,7 @@ def self.options_for(heading, path)
section.gsub!(/^\s*(.*?)\s*(?:#.*)?$/, '\1')
# Convert to a hash
- returning({}) do |options|
+ {}.tap do |options|
lines = section.split(/\n+/)
while line = lines.shift
if line =~ /(.*?)\s*=\s*(.*)/
@@ -175,7 +175,7 @@ def self.get_models_to_class_ids #:nodoc:
hash
else
# We can't raise here because you may be generating the configuration for the first time
- Ultrasphinx.say "configuration file not found for #{RAILS_ENV.inspect} environment"
+ Ultrasphinx.say "configuration file not found for #{Rails.env.inspect} environment"
Ultrasphinx.say "please run 'rake ultrasphinx:configure'"
end
end
@@ -198,6 +198,17 @@ def test_text_filter
S.new(:class_names => 'Seller', :filters => {'company_name' => 'seller17'}).run.size
)
end
+
+ def test_exclusion_filter
+ assert_equal(
+ Seller.count(:conditions => 'user_id = 17'),
+ S.new(:class_names => 'Seller', :filters => { 'user_id' => { 'value' => 17, 'exclude' => false } }).run.size
+ )
+ assert_equal(
+ Seller.count(:conditions => 'user_id != 17'),
+ S.new(:class_names => 'Seller', :filters => { 'user_id' => { 'value' => 17, 'exclude' => true } }).run.size
+ )
+ end
def test_invalid_filter
assert_raises(Ultrasphinx::UsageError) do
@@ -378,4 +389,4 @@ def test_distance_decending
assert_match /Kailua Beach Park/, @s.first.name
assert_in_delta 16940, @s.first.distance, 40
end
-end
+end
Oops, something went wrong.

0 comments on commit 3b8275d

Please sign in to comment.