DrMark-thinking-sphinx 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|