DrMark-thinking-sphinx 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +6 -1
- data/lib/riddle/client/filter.rb +1 -1
- data/lib/riddle/client.rb +57 -11
- data/lib/riddle.rb +10 -6
- data/lib/thinking_sphinx/active_record/delta.rb +1 -4
- data/lib/thinking_sphinx/active_record.rb +6 -5
- data/lib/thinking_sphinx/configuration.rb +3 -3
- data/lib/thinking_sphinx/index.rb +6 -1
- data/lib/thinking_sphinx/search.rb +132 -85
- data/lib/thinking_sphinx.rb +5 -3
- data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +2 -1
- data/spec/unit/thinking_sphinx/active_record/search_spec.rb +1 -1
- data/spec/unit/thinking_sphinx/active_record_spec.rb +33 -11
- data/spec/unit/thinking_sphinx/index_spec.rb +38 -1
- data/spec/unit/thinking_sphinx/search_spec.rb +31 -6
- data/spec/unit/thinking_sphinx_spec.rb +2 -1
- metadata +2 -2
data/README
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
|
5
5
|
First, if you haven't done so already, check out the main usage[http://ts.freelancing-gods.com/usage.html] page. Once you've done that, the next place to look for information is the specific method docs - ThinkingSphinx::Search and ThinkingSphinx::Index::Builder in particular.
|
6
6
|
|
7
|
+
Keep in mind that while Thinking Sphinx works for ActiveRecord with Merb, it doesn't yet support DataMapper (although that is planned).
|
8
|
+
|
7
9
|
== Contributing
|
8
10
|
|
9
11
|
Fork on GitHub and after you've committed tested patches, send a pull request.
|
@@ -49,4 +51,7 @@ Since I first released this library, there's been quite a few people who have su
|
|
49
51
|
- Chris Heald
|
50
52
|
- Peter Vandenberk
|
51
53
|
- Josh French
|
52
|
-
- Andrew Bennett
|
54
|
+
- Andrew Bennett
|
55
|
+
- Jordan Fowler
|
56
|
+
- Seth Walker
|
57
|
+
- Joe Noon
|
data/lib/riddle/client/filter.rb
CHANGED
data/lib/riddle/client.rb
CHANGED
@@ -100,7 +100,7 @@ module Riddle
|
|
100
100
|
:match_mode, :sort_mode, :sort_by, :weights, :id_range, :filters,
|
101
101
|
:group_by, :group_function, :group_clause, :group_distinct, :cut_off,
|
102
102
|
:retry_count, :retry_delay, :anchor, :index_weights, :rank_mode,
|
103
|
-
:max_query_time, :field_weights
|
103
|
+
:max_query_time, :field_weights, :timeout
|
104
104
|
attr_reader :queue
|
105
105
|
|
106
106
|
# Can instantiate with a specific server and port - otherwise it assumes
|
@@ -134,10 +134,40 @@ module Riddle
|
|
134
134
|
@max_query_time = 0
|
135
135
|
# string keys are field names, integer values are weightings
|
136
136
|
@field_weights = {}
|
137
|
+
@timeout = 0
|
137
138
|
|
138
139
|
@queue = []
|
139
140
|
end
|
140
141
|
|
142
|
+
# Reset attributes and settings to defaults.
|
143
|
+
def reset
|
144
|
+
# defaults
|
145
|
+
@offset = 0
|
146
|
+
@limit = 20
|
147
|
+
@max_matches = 1000
|
148
|
+
@match_mode = :all
|
149
|
+
@sort_mode = :relevance
|
150
|
+
@sort_by = ''
|
151
|
+
@weights = []
|
152
|
+
@id_range = 0..0
|
153
|
+
@filters = []
|
154
|
+
@group_by = ''
|
155
|
+
@group_function = :day
|
156
|
+
@group_clause = '@group desc'
|
157
|
+
@group_distinct = ''
|
158
|
+
@cut_off = 0
|
159
|
+
@retry_count = 0
|
160
|
+
@retry_delay = 0
|
161
|
+
@anchor = {}
|
162
|
+
# string keys are index names, integer values are weightings
|
163
|
+
@index_weights = {}
|
164
|
+
@rank_mode = :proximity_bm25
|
165
|
+
@max_query_time = 0
|
166
|
+
# string keys are field names, integer values are weightings
|
167
|
+
@field_weights = {}
|
168
|
+
@timeout = 0
|
169
|
+
end
|
170
|
+
|
141
171
|
# Set the geo-anchor point - with the names of the attributes that contain
|
142
172
|
# the latitude and longitude (in radians), and the reference position.
|
143
173
|
# Note that for geocoding to work properly, you must also set
|
@@ -149,12 +179,12 @@ module Riddle
|
|
149
179
|
# Example:
|
150
180
|
# client.set_anchor('lat', -0.6591741, 'long', 2.530770)
|
151
181
|
#
|
152
|
-
def set_anchor(lat_attr, lat, long_attr, long)
|
182
|
+
def set_anchor(lat_attr, lat, long_attr, long, units='')
|
153
183
|
@anchor = {
|
154
184
|
:latitude_attribute => lat_attr,
|
155
|
-
:latitude => lat,
|
185
|
+
:latitude => (units == 'degrees' ? lat * Math::PI / 180 : lat),
|
156
186
|
:longitude_attribute => long_attr,
|
157
|
-
:longitude => long
|
187
|
+
:longitude => (units == 'degrees' ? long * Math::PI / 180 : long)
|
158
188
|
}
|
159
189
|
end
|
160
190
|
|
@@ -384,6 +414,26 @@ module Riddle
|
|
384
414
|
# Connects to the Sphinx daemon, and yields a socket to use. The socket is
|
385
415
|
# closed at the end of the block.
|
386
416
|
def connect(&block)
|
417
|
+
socket = nil
|
418
|
+
if @timeout == 0
|
419
|
+
socket = initialise_connection
|
420
|
+
else
|
421
|
+
begin
|
422
|
+
Timeout.timeout(@timeout) { socket = initialise_connection }
|
423
|
+
rescue Timeout::Error
|
424
|
+
raise Riddle::ConnectionError,
|
425
|
+
"Connection to #{@server} on #{@port} timed out after #{@timeout} seconds"
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
begin
|
430
|
+
yield socket
|
431
|
+
ensure
|
432
|
+
socket.close
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
def initialise_connection
|
387
437
|
socket = TCPSocket.new @server, @port
|
388
438
|
|
389
439
|
# Checking version
|
@@ -396,11 +446,7 @@ module Riddle
|
|
396
446
|
# Send version
|
397
447
|
socket.send [1].pack('N'), 0
|
398
448
|
|
399
|
-
|
400
|
-
yield socket
|
401
|
-
ensure
|
402
|
-
socket.close
|
403
|
-
end
|
449
|
+
socket
|
404
450
|
end
|
405
451
|
|
406
452
|
# Send a collection of messages, for a command type (eg, search, excerpts,
|
@@ -507,7 +553,7 @@ module Riddle
|
|
507
553
|
# Per Index Weights
|
508
554
|
message.append_int @index_weights.length
|
509
555
|
@index_weights.each do |key,val|
|
510
|
-
message.append_string key
|
556
|
+
message.append_string key.to_s
|
511
557
|
message.append_int val
|
512
558
|
end
|
513
559
|
|
@@ -517,7 +563,7 @@ module Riddle
|
|
517
563
|
# Per Field Weights
|
518
564
|
message.append_int @field_weights.length
|
519
565
|
@field_weights.each do |key,val|
|
520
|
-
message.append_string key
|
566
|
+
message.append_string key.to_s
|
521
567
|
message.append_int val
|
522
568
|
end
|
523
569
|
|
data/lib/riddle.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'timeout'
|
2
3
|
require 'riddle/client'
|
3
4
|
require 'riddle/client/filter'
|
4
5
|
require 'riddle/client/message'
|
@@ -9,14 +10,17 @@ module Riddle #:nodoc:
|
|
9
10
|
end
|
10
11
|
|
11
12
|
module Version #:nodoc:
|
12
|
-
Major
|
13
|
-
Minor
|
14
|
-
Tiny
|
13
|
+
Major = 0
|
14
|
+
Minor = 9
|
15
|
+
Tiny = 8
|
15
16
|
# Revision number for RubyForge's sake, taken from what Sphinx
|
16
17
|
# outputs to the command line.
|
17
|
-
Rev
|
18
|
+
Rev = 1371
|
19
|
+
# Release number to mark my own fixes, beyond feature parity with
|
20
|
+
# Sphinx itself.
|
21
|
+
Release = 0
|
18
22
|
|
19
|
-
String = [Major, Minor, Tiny].join('.')
|
20
|
-
GemVersion = [Major, Minor, Tiny, Rev].join('.')
|
23
|
+
String = [Major, Minor, Tiny].join('.')
|
24
|
+
GemVersion = [Major, Minor, Tiny, Rev, Release].join('.')
|
21
25
|
end
|
22
26
|
end
|
@@ -73,10 +73,7 @@ module ThinkingSphinx
|
|
73
73
|
# if running in the test environment.
|
74
74
|
#
|
75
75
|
def index_delta
|
76
|
-
|
77
|
-
!ThinkingSphinx.deltas_enabled?
|
78
|
-
return true
|
79
|
-
end
|
76
|
+
return true unless ThinkingSphinx.deltas_enabled?
|
80
77
|
|
81
78
|
configuration = ThinkingSphinx::Configuration.new
|
82
79
|
system "indexer --config #{configuration.config_file} --rotate #{self.class.indexes.first.name}_delta"
|
@@ -10,9 +10,8 @@ module ThinkingSphinx
|
|
10
10
|
module ActiveRecord
|
11
11
|
def self.included(base)
|
12
12
|
base.class_eval do
|
13
|
+
class_inheritable_array :indexes
|
13
14
|
class << self
|
14
|
-
attr_accessor :indexes
|
15
|
-
|
16
15
|
# Allows creation of indexes for Sphinx. If you don't do this, there
|
17
16
|
# isn't much point trying to search (or using this plugin at all,
|
18
17
|
# really).
|
@@ -65,10 +64,10 @@ module ThinkingSphinx
|
|
65
64
|
def define_index(&block)
|
66
65
|
return unless ThinkingSphinx.define_indexes?
|
67
66
|
|
68
|
-
|
67
|
+
self.indexes ||= []
|
69
68
|
index = Index.new(self, &block)
|
70
69
|
|
71
|
-
|
70
|
+
self.indexes << index
|
72
71
|
unless ThinkingSphinx.indexed_models.include?(self.name)
|
73
72
|
ThinkingSphinx.indexed_models << self.name
|
74
73
|
end
|
@@ -132,7 +131,9 @@ module ThinkingSphinx
|
|
132
131
|
"#{self.class.indexes.first.name}_delta",
|
133
132
|
['sphinx_deleted'],
|
134
133
|
{self.id => 1}
|
135
|
-
) if
|
134
|
+
) if ThinkingSphinx.deltas_enabled? &&
|
135
|
+
self.class.indexes.any? { |index| index.delta? } &&
|
136
|
+
self.delta?
|
136
137
|
end
|
137
138
|
end
|
138
139
|
end
|
@@ -11,7 +11,7 @@ module ThinkingSphinx
|
|
11
11
|
# query log file:: log/searchd.query.log
|
12
12
|
# pid file:: log/searchd.#{environment}.pid
|
13
13
|
# searchd files:: db/sphinx/#{environment}/
|
14
|
-
# address::
|
14
|
+
# address:: 127.0.0.1
|
15
15
|
# port:: 3312
|
16
16
|
# allow star:: false
|
17
17
|
# min prefix length:: 1
|
@@ -63,7 +63,7 @@ module ThinkingSphinx
|
|
63
63
|
self.query_log_file = "#{self.app_root}/log/searchd.query.log"
|
64
64
|
self.pid_file = "#{self.app_root}/log/searchd.#{environment}.pid"
|
65
65
|
self.searchd_file_path = "#{self.app_root}/db/sphinx/#{environment}"
|
66
|
-
self.address = "
|
66
|
+
self.address = "127.0.0.1"
|
67
67
|
self.port = 3312
|
68
68
|
self.allow_star = false
|
69
69
|
self.min_prefix_len = 1
|
@@ -165,7 +165,7 @@ searchd
|
|
165
165
|
}
|
166
166
|
|
167
167
|
begin
|
168
|
-
model_name.
|
168
|
+
model_name.camelize.constantize
|
169
169
|
rescue LoadError
|
170
170
|
model_name.gsub!(/.*[\/\\]/, '')
|
171
171
|
retry
|
@@ -41,6 +41,11 @@ module ThinkingSphinx
|
|
41
41
|
model.name.underscore.tr(':/\\', '_')
|
42
42
|
end
|
43
43
|
|
44
|
+
def empty?(part = :core)
|
45
|
+
config = ThinkingSphinx::Configuration.new
|
46
|
+
File.size?("#{config.searchd_file_path}/#{self.name}_#{part}.spa").nil?
|
47
|
+
end
|
48
|
+
|
44
49
|
def to_config(index, database_conf, charset_type)
|
45
50
|
# Set up associations and joins
|
46
51
|
link!
|
@@ -252,7 +257,7 @@ GROUP BY #{ (
|
|
252
257
|
|
253
258
|
unless @model.descends_from_active_record?
|
254
259
|
stored_class = @model.store_full_sti_class ? @model.name : @model.name.demodulize
|
255
|
-
builder.where("#{@model.inheritance_column} = '#{stored_class}'")
|
260
|
+
builder.where("#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)} = '#{stored_class}'")
|
256
261
|
end
|
257
262
|
|
258
263
|
@fields = builder.fields
|
@@ -5,7 +5,7 @@ module ThinkingSphinx
|
|
5
5
|
# Most times, you will just want a specific model's results - to search and
|
6
6
|
# search_for_ids methods will do the job in exactly the same manner when
|
7
7
|
# called from a model.
|
8
|
-
#
|
8
|
+
#
|
9
9
|
class Search
|
10
10
|
class << self
|
11
11
|
# Searches for results that match the parameters provided. Will only
|
@@ -14,16 +14,23 @@ module ThinkingSphinx
|
|
14
14
|
#
|
15
15
|
def search_for_ids(*args)
|
16
16
|
results, client = search_results(*args.clone)
|
17
|
-
|
17
|
+
|
18
18
|
options = args.extract_options!
|
19
19
|
page = options[:page] ? options[:page].to_i : 1
|
20
|
-
|
20
|
+
|
21
21
|
begin
|
22
|
-
pager = WillPaginate::Collection.
|
23
|
-
client.limit, results[:
|
24
|
-
|
22
|
+
pager = WillPaginate::Collection.create(page,
|
23
|
+
client.limit, results[:total_found] || 0) do |collection|
|
24
|
+
collection.replace results[:matches].collect { |match| match[:doc] }
|
25
|
+
collection.instance_variable_set :@total_entries, results[:total_found]
|
26
|
+
end
|
27
|
+
return (options[:include_raw] ? [pager, results] : pager)
|
25
28
|
rescue
|
26
|
-
|
29
|
+
if options[:include_raw]
|
30
|
+
return results[:matches].collect { |match| match[:doc] }, results
|
31
|
+
else
|
32
|
+
return results[:matches].collect { |match| match[:doc] }
|
33
|
+
end
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -35,11 +42,11 @@ module ThinkingSphinx
|
|
35
42
|
# just like paginate. The same parameters - :page and :per_page - work as
|
36
43
|
# expected, and the returned result set can be used by the will_paginate
|
37
44
|
# helper.
|
38
|
-
#
|
45
|
+
#
|
39
46
|
# == Basic Searching
|
40
47
|
#
|
41
48
|
# The simplest way of searching is straight text.
|
42
|
-
#
|
49
|
+
#
|
43
50
|
# ThinkingSphinx::Search.search "pat"
|
44
51
|
# ThinkingSphinx::Search.search "google"
|
45
52
|
# User.search "pat", :page => (params[:page] || 1)
|
@@ -47,14 +54,32 @@ module ThinkingSphinx
|
|
47
54
|
#
|
48
55
|
# If you specify :include, like in an #find call, this will be respected
|
49
56
|
# when loading the relevant models from the search results.
|
50
|
-
#
|
57
|
+
#
|
51
58
|
# User.search "pat", :include => :posts
|
52
59
|
#
|
60
|
+
# == Advanced Searching
|
61
|
+
#
|
62
|
+
# Sphinx supports 5 different matching modes. By default Thinking Sphinx
|
63
|
+
# uses :all, which unsurprisingly requires all the supplied search terms
|
64
|
+
# to match a result.
|
65
|
+
#
|
66
|
+
# Alternative modes include:
|
67
|
+
#
|
68
|
+
# User.search "pat allan", :match_mode => :any
|
69
|
+
# User.search "pat allan", :match_mode => :phrase
|
70
|
+
# User.search "pat | allan", :match_mode => :boolean
|
71
|
+
# User.search "@name pat | @username pat", :match_mode => :extended
|
72
|
+
#
|
73
|
+
# Any will find results with any of the search terms. Phrase treats the search
|
74
|
+
# terms a single phrase instead of individual words. Boolean and extended allow
|
75
|
+
# for more complex query syntax, refer to the sphinx documentation for further
|
76
|
+
# details.
|
77
|
+
#
|
53
78
|
# == Searching by Fields
|
54
|
-
#
|
79
|
+
#
|
55
80
|
# If you want to step it up a level, you can limit your search terms to
|
56
81
|
# specific fields:
|
57
|
-
#
|
82
|
+
#
|
58
83
|
# User.search :conditions => {:name => "pat"}
|
59
84
|
#
|
60
85
|
# This uses Sphinx's extended match mode, unless you specify a different
|
@@ -74,11 +99,11 @@ module ThinkingSphinx
|
|
74
99
|
# (not multi-model searching). With a single model, Thinking Sphinx
|
75
100
|
# can figure out what attributes and fields are available, so you can
|
76
101
|
# put it all in the :conditions hash, and it will sort it out.
|
77
|
-
#
|
102
|
+
#
|
78
103
|
# Node.search :conditions => {:parent_id => 10}
|
79
|
-
#
|
104
|
+
#
|
80
105
|
# Filters can be single values, arrays of values, or ranges.
|
81
|
-
#
|
106
|
+
#
|
82
107
|
# Article.search "East Timor", :conditions => {:rating => 3..5}
|
83
108
|
#
|
84
109
|
# == Excluding by Attributes
|
@@ -87,7 +112,7 @@ module ThinkingSphinx
|
|
87
112
|
# attribute values to exclude. This is done with the :without option:
|
88
113
|
#
|
89
114
|
# User.search :without => {:role_id => 1}
|
90
|
-
#
|
115
|
+
#
|
91
116
|
# == Sorting
|
92
117
|
#
|
93
118
|
# Sphinx can only sort by attributes, so generally you will need to avoid
|
@@ -112,13 +137,13 @@ module ThinkingSphinx
|
|
112
137
|
# detail though.
|
113
138
|
#
|
114
139
|
# == Grouping
|
115
|
-
#
|
140
|
+
#
|
116
141
|
# For this you can use the group_by, group_clause and group_function
|
117
142
|
# options - which are all directly linked to Sphinx's expectations. No
|
118
143
|
# magic from Thinking Sphinx. It can get a little tricky, so make sure
|
119
144
|
# you read all the relevant
|
120
145
|
# documentation[http://sphinxsearch.com/doc.html#clustering] first.
|
121
|
-
#
|
146
|
+
#
|
122
147
|
# Yes this section will be expanded, but this is a start.
|
123
148
|
#
|
124
149
|
# == Geo/Location Searching
|
@@ -128,11 +153,11 @@ module ThinkingSphinx
|
|
128
153
|
# take advantage of this, you will need to have both of those values in
|
129
154
|
# attributes. To search with that point, you can then use one of the
|
130
155
|
# following syntax examples:
|
131
|
-
#
|
156
|
+
#
|
132
157
|
# Address.search "Melbourne", :geo => [1.4, -2.217]
|
133
158
|
# Address.search "Australia", :geo => [-0.55, 3.108],
|
134
159
|
# :latitude_attr => "latit", :longitude_attr => "longit"
|
135
|
-
#
|
160
|
+
#
|
136
161
|
# The first example applies when your latitude and longitude attributes
|
137
162
|
# are named any of lat, latitude, lon, long or longitude. If that's not
|
138
163
|
# the case, you will need to explicitly state them in your search, _or_
|
@@ -141,17 +166,17 @@ module ThinkingSphinx
|
|
141
166
|
# define_index do
|
142
167
|
# has :latit # Float column, stored in radians
|
143
168
|
# has :longit # Float column, stored in radians
|
144
|
-
#
|
169
|
+
#
|
145
170
|
# set_property :latitude_attr => "latit"
|
146
171
|
# set_property :longitude_attr => "longit"
|
147
172
|
# end
|
148
|
-
#
|
173
|
+
#
|
149
174
|
# Now, geo-location searching really only has an affect if you have a
|
150
175
|
# filter, sort or grouping clause related to it - otherwise it's just a
|
151
176
|
# normal search. To make use of the positioning difference, use the
|
152
177
|
# special attribute "@geodist" in any of your filters or sorting or grouping
|
153
178
|
# clauses.
|
154
|
-
#
|
179
|
+
#
|
155
180
|
# And don't forget - both the latitude and longitude you use in your
|
156
181
|
# search, and the values in your indexes, need to be stored as a float in radians,
|
157
182
|
# _not_ degrees. Keep in mind that if you do this conversion in SQL
|
@@ -161,75 +186,82 @@ module ThinkingSphinx
|
|
161
186
|
# has 'RADIANS(lat)', :as => :lat, :type => :float
|
162
187
|
# # ...
|
163
188
|
# end
|
164
|
-
#
|
189
|
+
#
|
165
190
|
def search(*args)
|
166
191
|
results, client = search_results(*args.clone)
|
167
|
-
|
192
|
+
|
168
193
|
::ActiveRecord::Base.logger.error(
|
169
194
|
"Sphinx Error: #{results[:error]}"
|
170
195
|
) if results[:error]
|
171
|
-
|
196
|
+
|
172
197
|
options = args.extract_options!
|
173
198
|
klass = options[:class]
|
174
199
|
page = options[:page] ? options[:page].to_i : 1
|
175
|
-
|
200
|
+
|
176
201
|
begin
|
177
|
-
pager = WillPaginate::Collection.
|
178
|
-
client.limit, results[:total] || 0)
|
179
|
-
|
202
|
+
pager = WillPaginate::Collection.create(page,
|
203
|
+
client.limit, results[:total] || 0) do |collection|
|
204
|
+
collection.replace instances_from_results(results[:matches], options, klass)
|
205
|
+
collection.instance_variable_set :@total_entries, results[:total_found]
|
206
|
+
end
|
207
|
+
return (options[:include_raw] ? [pager, results] : pager)
|
180
208
|
rescue StandardError => err
|
181
|
-
|
209
|
+
if options[:include_raw]
|
210
|
+
return instances_from_results(results[:matches], options, klass), results
|
211
|
+
else
|
212
|
+
return instances_from_results(results[:matches], options, klass)
|
213
|
+
end
|
182
214
|
end
|
183
215
|
end
|
184
|
-
|
216
|
+
|
185
217
|
# Checks if a document with the given id exists within a specific index.
|
186
218
|
# Expected parameters:
|
187
219
|
#
|
188
220
|
# - ID of the document
|
189
221
|
# - Index to check within
|
190
222
|
# - Options hash (defaults to {})
|
191
|
-
#
|
223
|
+
#
|
192
224
|
# Example:
|
193
|
-
#
|
225
|
+
#
|
194
226
|
# ThinkingSphinx::Search.search_for_id(10, "user_core", :class => User)
|
195
|
-
#
|
227
|
+
#
|
196
228
|
def search_for_id(*args)
|
197
229
|
options = args.extract_options!
|
198
230
|
client = client_from_options options
|
199
|
-
|
231
|
+
|
200
232
|
query, filters = search_conditions(
|
201
233
|
options[:class], options[:conditions] || {}
|
202
234
|
)
|
203
235
|
client.filters += filters
|
204
236
|
client.match_mode = :extended unless query.empty?
|
205
237
|
client.id_range = args.first..args.first
|
206
|
-
|
238
|
+
|
207
239
|
begin
|
208
240
|
return client.query(query, args[1])[:matches].length > 0
|
209
241
|
rescue Errno::ECONNREFUSED => err
|
210
242
|
raise ThinkingSphinx::ConnectionError, "Connection to Sphinx Daemon (searchd) failed."
|
211
243
|
end
|
212
244
|
end
|
213
|
-
|
245
|
+
|
214
246
|
private
|
215
|
-
|
247
|
+
|
216
248
|
# This method handles the common search functionality, and returns both
|
217
249
|
# the result hash and the client. Not super elegant, but it'll do for
|
218
250
|
# the moment.
|
219
|
-
#
|
251
|
+
#
|
220
252
|
def search_results(*args)
|
221
253
|
options = args.extract_options!
|
222
254
|
client = client_from_options options
|
223
|
-
|
255
|
+
|
224
256
|
query, filters = search_conditions(
|
225
257
|
options[:class], options[:conditions] || {}
|
226
258
|
)
|
227
259
|
client.filters += filters
|
228
260
|
client.match_mode = :extended unless query.empty?
|
229
261
|
query = args.join(" ") + query
|
230
|
-
|
262
|
+
|
231
263
|
set_sort_options! client, options
|
232
|
-
|
264
|
+
|
233
265
|
client.limit = options[:per_page].to_i if options[:per_page]
|
234
266
|
page = options[:page] ? options[:page].to_i : 1
|
235
267
|
client.offset = (page - 1) * client.limit
|
@@ -241,10 +273,21 @@ module ThinkingSphinx
|
|
241
273
|
rescue Errno::ECONNREFUSED => err
|
242
274
|
raise ThinkingSphinx::ConnectionError, "Connection to Sphinx Daemon (searchd) failed."
|
243
275
|
end
|
244
|
-
|
276
|
+
|
245
277
|
return results, client
|
246
278
|
end
|
247
|
-
|
279
|
+
|
280
|
+
# This function loops over the records and appends a 'distance' variable to each one with
|
281
|
+
# the value from Sphinx
|
282
|
+
def append_distances(instances, results, distance_name)
|
283
|
+
instances.each_with_index do |record, index|
|
284
|
+
if record
|
285
|
+
distance = (results[index][:attributes]['@geodist'] or nil)
|
286
|
+
record.instance_variable_get('@attributes')["#{distance_name}"] = distance
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
248
291
|
def instances_from_results(results, options = {}, klass = nil)
|
249
292
|
if klass.nil?
|
250
293
|
results.collect { |result| instance_from_result result, options }
|
@@ -256,43 +299,47 @@ module ThinkingSphinx
|
|
256
299
|
:include => options[:include],
|
257
300
|
:select => options[:select]
|
258
301
|
)
|
259
|
-
ids.collect { |obj_id| instances.detect { |obj| obj.id == obj_id } }
|
302
|
+
final_instances = ids.collect { |obj_id| instances.detect { |obj| obj.id == obj_id } }
|
303
|
+
|
304
|
+
final_instances = append_distances(final_instances, results, options[:distance_name]) if options[:distance_name] && (results.collect { |result| result[:attributes]['@geodist'] }.length > 0)
|
305
|
+
|
306
|
+
return final_instances
|
260
307
|
end
|
261
308
|
end
|
262
|
-
|
309
|
+
|
263
310
|
# Either use the provided class to instantiate a result from a model, or
|
264
311
|
# get the result's CRC value and determine the class from that.
|
265
|
-
#
|
312
|
+
#
|
266
313
|
def instance_from_result(result, options)
|
267
314
|
class_from_crc(result[:attributes]["class_crc"]).find(
|
268
315
|
result[:doc], :include => options[:include], :select => options[:select]
|
269
316
|
)
|
270
317
|
end
|
271
|
-
|
318
|
+
|
272
319
|
# Convert a CRC value to the corresponding class.
|
273
|
-
#
|
320
|
+
#
|
274
321
|
def class_from_crc(crc)
|
275
322
|
unless @models_by_crc
|
276
323
|
Configuration.new.load_models
|
277
|
-
|
324
|
+
|
278
325
|
@models_by_crc = ThinkingSphinx.indexed_models.inject({}) do |hash, model|
|
279
326
|
hash[model.constantize.to_crc32] = model
|
280
327
|
hash
|
281
328
|
end
|
282
329
|
end
|
283
|
-
|
330
|
+
|
284
331
|
@models_by_crc[crc].constantize
|
285
332
|
end
|
286
|
-
|
333
|
+
|
287
334
|
# Set all the appropriate settings for the client, using the provided
|
288
335
|
# options hash.
|
289
336
|
#
|
290
|
-
def client_from_options(options)
|
337
|
+
def client_from_options(options = {})
|
291
338
|
config = ThinkingSphinx::Configuration.new
|
292
339
|
client = Riddle::Client.new config.address, config.port
|
293
340
|
klass = options[:class]
|
294
341
|
index_options = klass ? klass.indexes.last.options : {}
|
295
|
-
|
342
|
+
|
296
343
|
[
|
297
344
|
:max_matches, :match_mode, :sort_mode, :sort_by, :id_range,
|
298
345
|
:group_by, :group_function, :group_clause, :group_distinct, :cut_off,
|
@@ -304,9 +351,9 @@ module ThinkingSphinx
|
|
304
351
|
options[key] || index_options[key] || client.send(key)
|
305
352
|
)
|
306
353
|
end
|
307
|
-
|
354
|
+
|
308
355
|
client.anchor = anchor_conditions(klass, options) || {} if client.anchor.empty?
|
309
|
-
|
356
|
+
|
310
357
|
client.filters << Riddle::Client::Filter.new(
|
311
358
|
"sphinx_deleted", [0]
|
312
359
|
)
|
@@ -314,20 +361,20 @@ module ThinkingSphinx
|
|
314
361
|
client.filters << Riddle::Client::Filter.new(
|
315
362
|
"class_crc", options[:classes].collect { |klass| klass.to_crc32 }
|
316
363
|
) if options[:classes]
|
317
|
-
|
364
|
+
|
318
365
|
# normal attribute filters
|
319
366
|
client.filters += options[:with].collect { |attr,val|
|
320
367
|
Riddle::Client::Filter.new attr.to_s, filter_value(val)
|
321
368
|
} if options[:with]
|
322
|
-
|
369
|
+
|
323
370
|
# exclusive attribute filters
|
324
371
|
client.filters += options[:without].collect { |attr,val|
|
325
372
|
Riddle::Client::Filter.new attr.to_s, filter_value(val), true
|
326
373
|
} if options[:without]
|
327
|
-
|
374
|
+
|
328
375
|
client
|
329
376
|
end
|
330
|
-
|
377
|
+
|
331
378
|
def filter_value(value)
|
332
379
|
case value
|
333
380
|
when Range
|
@@ -338,18 +385,18 @@ module ThinkingSphinx
|
|
338
385
|
Array(value)
|
339
386
|
end
|
340
387
|
end
|
341
|
-
|
388
|
+
|
342
389
|
# Translate field and attribute conditions to the relevant search string
|
343
390
|
# and filters.
|
344
|
-
#
|
391
|
+
#
|
345
392
|
def search_conditions(klass, conditions={})
|
346
393
|
attributes = klass ? klass.indexes.collect { |index|
|
347
394
|
index.attributes.collect { |attrib| attrib.unique_name }
|
348
395
|
}.flatten : []
|
349
|
-
|
396
|
+
|
350
397
|
search_string = ""
|
351
398
|
filters = []
|
352
|
-
|
399
|
+
|
353
400
|
conditions.each do |key,val|
|
354
401
|
if attributes.include?(key.to_sym)
|
355
402
|
filters << Riddle::Client::Filter.new(
|
@@ -360,48 +407,48 @@ module ThinkingSphinx
|
|
360
407
|
search_string << "@#{key} #{val} "
|
361
408
|
end
|
362
409
|
end
|
363
|
-
|
410
|
+
|
364
411
|
filters << Riddle::Client::Filter.new(
|
365
412
|
"class_crc", [klass.to_crc32]
|
366
413
|
) if klass
|
367
|
-
|
414
|
+
|
368
415
|
return search_string, filters
|
369
416
|
end
|
370
|
-
|
417
|
+
|
371
418
|
# Return the appropriate latitude and longitude values, depending on
|
372
419
|
# whether the relevant attributes have been defined, and also whether
|
373
420
|
# there's actually any values.
|
374
|
-
#
|
421
|
+
#
|
375
422
|
def anchor_conditions(klass, options)
|
376
423
|
attributes = klass ? klass.indexes.collect { |index|
|
377
424
|
index.attributes.collect { |attrib| attrib.unique_name }
|
378
425
|
}.flatten : []
|
379
|
-
|
426
|
+
|
380
427
|
lat_attr = klass ? klass.indexes.collect { |index|
|
381
428
|
index.options[:latitude_attr]
|
382
429
|
}.compact.first : nil
|
383
|
-
|
430
|
+
|
384
431
|
lon_attr = klass ? klass.indexes.collect { |index|
|
385
432
|
index.options[:longitude_attr]
|
386
433
|
}.compact.first : nil
|
387
|
-
|
434
|
+
|
388
435
|
lat_attr = options[:latitude_attr] if options[:latitude_attr]
|
389
436
|
lat_attr ||= :lat if attributes.include?(:lat)
|
390
437
|
lat_attr ||= :latitude if attributes.include?(:latitude)
|
391
|
-
|
438
|
+
|
392
439
|
lon_attr = options[:longitude_attr] if options[:longitude_attr]
|
393
440
|
lon_attr ||= :lon if attributes.include?(:lon)
|
394
441
|
lon_attr ||= :long if attributes.include?(:long)
|
395
442
|
lon_attr ||= :longitude if attributes.include?(:longitude)
|
396
|
-
|
443
|
+
|
397
444
|
lat = options[:lat]
|
398
445
|
lon = options[:lon]
|
399
|
-
|
446
|
+
|
400
447
|
if options[:geo]
|
401
448
|
lat = options[:geo].first
|
402
449
|
lon = options[:geo].last
|
403
450
|
end
|
404
|
-
|
451
|
+
|
405
452
|
lat && lon ? {
|
406
453
|
:latitude_attribute => lat_attr,
|
407
454
|
:latitude => lat,
|
@@ -409,19 +456,19 @@ module ThinkingSphinx
|
|
409
456
|
:longitude => lon
|
410
457
|
} : nil
|
411
458
|
end
|
412
|
-
|
459
|
+
|
413
460
|
# Set the sort options using the :order key as well as the appropriate
|
414
461
|
# Riddle settings.
|
415
|
-
#
|
462
|
+
#
|
416
463
|
def set_sort_options!(client, options)
|
417
464
|
klass = options[:class]
|
418
465
|
fields = klass ? klass.indexes.collect { |index|
|
419
466
|
index.fields.collect { |field| field.unique_name }
|
420
467
|
}.flatten : []
|
421
|
-
|
468
|
+
|
422
469
|
case order = options[:order]
|
423
470
|
when Symbol
|
424
|
-
client.sort_mode
|
471
|
+
client.sort_mode = :attr_asc if client.sort_mode == :relevance || client.sort_mode.nil?
|
425
472
|
if fields.include?(order)
|
426
473
|
client.sort_by = order.to_s.concat("_sort")
|
427
474
|
else
|
@@ -433,23 +480,23 @@ module ThinkingSphinx
|
|
433
480
|
else
|
434
481
|
# do nothing
|
435
482
|
end
|
436
|
-
|
483
|
+
|
437
484
|
client.sort_mode = :attr_asc if client.sort_mode == :asc
|
438
485
|
client.sort_mode = :attr_desc if client.sort_mode == :desc
|
439
486
|
end
|
440
|
-
|
487
|
+
|
441
488
|
# Search through a collection of fields and translate any appearances
|
442
489
|
# of them in a string to their attribute equivalent for sorting.
|
443
|
-
#
|
490
|
+
#
|
444
491
|
def sorted_fields_to_attributes(string, fields)
|
445
492
|
fields.each { |field|
|
446
493
|
string.gsub!(/(^|\s)#{field}(,?\s|$)/) { |match|
|
447
494
|
match.gsub field.to_s, field.to_s.concat("_sort")
|
448
495
|
}
|
449
496
|
}
|
450
|
-
|
497
|
+
|
451
498
|
string
|
452
499
|
end
|
453
500
|
end
|
454
501
|
end
|
455
|
-
end
|
502
|
+
end
|
data/lib/thinking_sphinx.rb
CHANGED
@@ -20,7 +20,7 @@ module ThinkingSphinx
|
|
20
20
|
module Version #:nodoc:
|
21
21
|
Major = 0
|
22
22
|
Minor = 9
|
23
|
-
Tiny =
|
23
|
+
Tiny = 8
|
24
24
|
|
25
25
|
String = [Major, Minor, Tiny].join('.')
|
26
26
|
end
|
@@ -52,10 +52,12 @@ module ThinkingSphinx
|
|
52
52
|
@@define_indexes = value
|
53
53
|
end
|
54
54
|
|
55
|
+
@@deltas_enabled = nil
|
56
|
+
|
55
57
|
# Check if delta indexing is enabled.
|
56
58
|
#
|
57
59
|
def self.deltas_enabled?
|
58
|
-
@@deltas_enabled
|
60
|
+
@@deltas_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@deltas_enabled.nil?
|
59
61
|
@@deltas_enabled == true
|
60
62
|
end
|
61
63
|
|
@@ -79,4 +81,4 @@ module ThinkingSphinx
|
|
79
81
|
"SELECT @@global.sql_mode, @@session.sql_mode;"
|
80
82
|
).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
|
81
83
|
end
|
82
|
-
end
|
84
|
+
end
|
@@ -167,6 +167,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
|
|
167
167
|
end
|
168
168
|
|
169
169
|
it "shouldn't index if the environment is 'test'" do
|
170
|
+
ThinkingSphinx.unstub_method(:deltas_enabled?)
|
170
171
|
ThinkingSphinx::Configuration.stub_method(:environment => "test")
|
171
172
|
|
172
173
|
@person.send(:index_delta)
|
@@ -182,4 +183,4 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
|
|
182
183
|
)
|
183
184
|
end
|
184
185
|
end
|
185
|
-
end
|
186
|
+
end
|
@@ -125,12 +125,6 @@ describe "ThinkingSphinx::ActiveRecord" do
|
|
125
125
|
@person.stub_method(:in_core_index? => true)
|
126
126
|
end
|
127
127
|
|
128
|
-
# after :each do
|
129
|
-
# ThinkingSphinx::Configuration.unstub_method(:new)
|
130
|
-
# Riddle::Client.unstub_method(:new)
|
131
|
-
# Person.indexes.each { |index| index.unstub_method(:delta?) }
|
132
|
-
# end
|
133
|
-
|
134
128
|
it "should create a client using the Configuration's address and port" do
|
135
129
|
@person.toggle_deleted
|
136
130
|
|
@@ -157,7 +151,8 @@ describe "ThinkingSphinx::ActiveRecord" do
|
|
157
151
|
)
|
158
152
|
end
|
159
153
|
|
160
|
-
it "should update the delta index's deleted flag if delta
|
154
|
+
it "should update the delta index's deleted flag if delta indexes are enabled and the instance's delta is true" do
|
155
|
+
ThinkingSphinx.stub_method(:deltas_enabled? => true)
|
161
156
|
Person.indexes.each { |index| index.stub_method(:delta? => true) }
|
162
157
|
@person.delta = true
|
163
158
|
|
@@ -168,7 +163,8 @@ describe "ThinkingSphinx::ActiveRecord" do
|
|
168
163
|
)
|
169
164
|
end
|
170
165
|
|
171
|
-
it "should not update the delta index's deleted flag if delta
|
166
|
+
it "should not update the delta index's deleted flag if delta indexes are enabled and the instance's delta is false" do
|
167
|
+
ThinkingSphinx.stub_method(:deltas_enabled? => true)
|
172
168
|
Person.indexes.each { |index| index.stub_method(:delta? => true) }
|
173
169
|
@person.delta = false
|
174
170
|
|
@@ -179,7 +175,8 @@ describe "ThinkingSphinx::ActiveRecord" do
|
|
179
175
|
)
|
180
176
|
end
|
181
177
|
|
182
|
-
it "should not update the delta index's deleted flag if delta
|
178
|
+
it "should not update the delta index's deleted flag if delta indexes are enabled and the instance's delta is equivalent to false" do
|
179
|
+
ThinkingSphinx.stub_method(:deltas_enabled? => true)
|
183
180
|
Person.indexes.each { |index| index.stub_method(:delta? => true) }
|
184
181
|
@person.delta = 0
|
185
182
|
|
@@ -190,7 +187,20 @@ describe "ThinkingSphinx::ActiveRecord" do
|
|
190
187
|
)
|
191
188
|
end
|
192
189
|
|
193
|
-
it "shouldn't update the delta index if delta
|
190
|
+
it "shouldn't update the delta index if delta indexes are disabled" do
|
191
|
+
ThinkingSphinx.stub_method(:deltas_enabled? => true)
|
192
|
+
@person.toggle_deleted
|
193
|
+
|
194
|
+
@client.should_not have_received(:update).with(
|
195
|
+
"person_delta", ["sphinx_deleted"], {@person.id => 1}
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should not update the delta index if delta indexing is disabled" do
|
200
|
+
ThinkingSphinx.stub_method(:deltas_enabled? => false)
|
201
|
+
Person.indexes.each { |index| index.stub_method(:delta? => true) }
|
202
|
+
@person.delta = true
|
203
|
+
|
194
204
|
@person.toggle_deleted
|
195
205
|
|
196
206
|
@client.should_not have_received(:update).with(
|
@@ -198,4 +208,16 @@ describe "ThinkingSphinx::ActiveRecord" do
|
|
198
208
|
)
|
199
209
|
end
|
200
210
|
end
|
201
|
-
|
211
|
+
|
212
|
+
describe "indexes in the inheritance chain (STI)" do
|
213
|
+
it "should hand defined indexes on a class down to its child classes" do
|
214
|
+
Child.indexes.should include(*Person.indexes)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should allow associations to other STI models" do
|
218
|
+
Child.indexes.last.link!
|
219
|
+
sql = Child.indexes.last.to_sql.gsub('$start', '0').gsub('$end', '100')
|
220
|
+
lambda { Child.connection.execute(sql) }.should_not raise_error(ActiveRecord::StatementInvalid)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -207,7 +207,7 @@ describe ThinkingSphinx::Index do
|
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
210
|
-
describe "infix_fields" do
|
210
|
+
describe "infix_fields method" do
|
211
211
|
before :each do
|
212
212
|
@index = ThinkingSphinx::Index.new(Person)
|
213
213
|
|
@@ -227,4 +227,41 @@ describe ThinkingSphinx::Index do
|
|
227
227
|
@index.infix_fields.should_not include(@field_b)
|
228
228
|
end
|
229
229
|
end
|
230
|
+
|
231
|
+
describe "empty? method" do
|
232
|
+
before :each do
|
233
|
+
@index = ThinkingSphinx::Index.new(Person)
|
234
|
+
config = ThinkingSphinx::Configuration.new
|
235
|
+
|
236
|
+
`mkdir -p #{config.searchd_file_path}`
|
237
|
+
@file_path = "#{config.searchd_file_path}/#{@index.name}_core.spa"
|
238
|
+
end
|
239
|
+
|
240
|
+
after :each do
|
241
|
+
`rm #{@file_path}` if File.exists?(@file_path)
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should return true if the core index files are empty" do
|
245
|
+
`touch #{@file_path}`
|
246
|
+
@index.should be_empty
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should return true if the core index files don't exist" do
|
250
|
+
@index.should be_empty
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should return false if the core index files aren't empty" do
|
254
|
+
`echo 'a' > #{@file_path}`
|
255
|
+
@index.should_not be_empty
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should check the delta files if specified" do
|
259
|
+
@index.should be_empty(:delta)
|
260
|
+
|
261
|
+
`echo 'a' > #{@file_path.gsub(/_core.spa$/, '_delta.spa')}`
|
262
|
+
@index.should_not be_empty(:delta)
|
263
|
+
|
264
|
+
`rm #{@file_path}` if File.exists?(@file_path.gsub(/_core.spa$/, '_delta.spa'))
|
265
|
+
end
|
266
|
+
end
|
230
267
|
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'spec/spec_helper'
|
2
2
|
|
3
3
|
describe ThinkingSphinx::Search do
|
4
|
+
describe "search method" do
|
5
|
+
it "should actually have some tests"
|
6
|
+
end
|
7
|
+
|
4
8
|
describe "search_for_id method" do
|
5
9
|
before :each do
|
6
10
|
@client = Riddle::Client.stub_instance(
|
@@ -82,10 +86,14 @@ describe ThinkingSphinx::Search do
|
|
82
86
|
@person_b = Person.stub_instance
|
83
87
|
@person_c = Person.stub_instance
|
84
88
|
|
89
|
+
@person_a.stub_method(:attributes => [])
|
90
|
+
@person_b.stub_method(:attributes => [])
|
91
|
+
@person_c.stub_method(:attributes => [])
|
92
|
+
|
85
93
|
@results = [
|
86
|
-
{:doc => @person_a.id},
|
87
|
-
{:doc => @person_b.id},
|
88
|
-
{:doc => @person_c.id}
|
94
|
+
{:doc => @person_a.id, :attributes => {'@geodist' => 10}},
|
95
|
+
{:doc => @person_b.id, :attributes => {'@geodist' => 10}},
|
96
|
+
{:doc => @person_c.id, :attributes => {'@geodist' => 10}}
|
89
97
|
]
|
90
98
|
|
91
99
|
Person.stub_method(
|
@@ -105,13 +113,13 @@ describe ThinkingSphinx::Search do
|
|
105
113
|
)
|
106
114
|
|
107
115
|
ThinkingSphinx::Search.should have_received(:instance_from_result).with(
|
108
|
-
{:doc => @person_a.id}, {}
|
116
|
+
{:doc => @person_a.id, :attributes => {'@geodist' => 10}}, {}
|
109
117
|
)
|
110
118
|
ThinkingSphinx::Search.should have_received(:instance_from_result).with(
|
111
|
-
{:doc => @person_b.id}, {}
|
119
|
+
{:doc => @person_b.id, :attributes => {'@geodist' => 10}}, {}
|
112
120
|
)
|
113
121
|
ThinkingSphinx::Search.should have_received(:instance_from_result).with(
|
114
|
-
{:doc => @person_c.id}, {}
|
122
|
+
{:doc => @person_c.id, :attributes => {'@geodist' => 10}}, {}
|
115
123
|
)
|
116
124
|
end
|
117
125
|
|
@@ -159,5 +167,22 @@ describe ThinkingSphinx::Search do
|
|
159
167
|
:instances_from_results, @results, {:select => :fields}, Person
|
160
168
|
).should == [@person_a, @person_b, @person_c]
|
161
169
|
end
|
170
|
+
|
171
|
+
it "should run the append distances function when distance_name is passed" do
|
172
|
+
ThinkingSphinx::Search.stub_method(:append_distances => @results)
|
173
|
+
|
174
|
+
ThinkingSphinx::Search.send(
|
175
|
+
:instances_from_results, @results, {:distance_name => 'distance'}, Person
|
176
|
+
)
|
177
|
+
|
178
|
+
Person.should have_received(:find).with(
|
179
|
+
:all,
|
180
|
+
:conditions => {:id => [@person_a.id, @person_b.id, @person_c.id]},
|
181
|
+
:include => nil,
|
182
|
+
:select => nil
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should have a test for the append_distances function"
|
162
187
|
end
|
163
188
|
end
|
@@ -24,6 +24,7 @@ describe ThinkingSphinx do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should index deltas by default" do
|
27
|
+
ThinkingSphinx.deltas_enabled = nil
|
27
28
|
ThinkingSphinx.deltas_enabled?.should be_true
|
28
29
|
end
|
29
30
|
|
@@ -104,4 +105,4 @@ describe ThinkingSphinx do
|
|
104
105
|
end
|
105
106
|
end
|
106
107
|
end
|
107
|
-
end
|
108
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: DrMark-thinking-sphinx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-07-08 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|