oedipus 0.0.1.pre1 → 0.0.1.pre2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.md +235 -44
- data/Rakefile +25 -0
- data/ext/oedipus/extconf.rb +72 -0
- data/ext/oedipus/oedipus.c +239 -0
- data/ext/oedipus/oedipus.h +50 -0
- data/lib/oedipus/comparison/between.rb +26 -0
- data/lib/oedipus/comparison/equal.rb +21 -0
- data/lib/oedipus/comparison/gt.rb +21 -0
- data/lib/oedipus/comparison/gte.rb +21 -0
- data/lib/oedipus/comparison/in.rb +21 -0
- data/lib/oedipus/comparison/lt.rb +21 -0
- data/lib/oedipus/comparison/lte.rb +21 -0
- data/lib/oedipus/comparison/not.rb +25 -0
- data/lib/oedipus/comparison/not_equal.rb +21 -0
- data/lib/oedipus/comparison/not_in.rb +21 -0
- data/lib/oedipus/comparison/outside.rb +26 -0
- data/lib/oedipus/comparison/shortcuts.rb +144 -0
- data/lib/oedipus/comparison.rb +88 -0
- data/lib/oedipus/connection.rb +91 -13
- data/lib/oedipus/connection_error.rb +14 -0
- data/lib/oedipus/index.rb +189 -46
- data/lib/oedipus/query_builder.rb +97 -4
- data/lib/oedipus/version.rb +1 -1
- data/lib/oedipus.rb +24 -7
- data/oedipus.gemspec +4 -5
- data/spec/integration/connection_spec.rb +58 -0
- data/spec/integration/index_spec.rb +353 -0
- data/spec/spec_helper.rb +2 -23
- data/spec/support/test_harness.rb +30 -9
- data/spec/unit/comparison/between_spec.rb +36 -0
- data/spec/unit/comparison/equal_spec.rb +22 -0
- data/spec/unit/comparison/gt_spec.rb +22 -0
- data/spec/unit/comparison/gte_spec.rb +22 -0
- data/spec/unit/comparison/in_spec.rb +22 -0
- data/spec/unit/comparison/lt_spec.rb +22 -0
- data/spec/unit/comparison/lte_spec.rb +22 -0
- data/spec/unit/comparison/not_equal_spec.rb +22 -0
- data/spec/unit/comparison/not_in_spec.rb +22 -0
- data/spec/unit/comparison/not_spec.rb +37 -0
- data/spec/unit/comparison/outside_spec.rb +36 -0
- data/spec/unit/comparison/shortcuts_spec.rb +125 -0
- data/spec/unit/comparison_spec.rb +109 -0
- data/spec/unit/query_builder_spec.rb +150 -0
- metadata +68 -19
- data/lib/oedipus/mysql/client.rb +0 -136
- data/spec/unit/connection_spec.rb +0 -36
- data/spec/unit/index_spec.rb +0 -85
data/lib/oedipus/index.rb
CHANGED
@@ -11,7 +11,6 @@ module Oedipus
|
|
11
11
|
# Representation of a search index for querying.
|
12
12
|
class Index
|
13
13
|
attr_reader :name
|
14
|
-
attr_reader :attributes
|
15
14
|
|
16
15
|
# Initialize the index named +name+ on the connection +conn+.
|
17
16
|
#
|
@@ -21,16 +20,15 @@ module Oedipus
|
|
21
20
|
# @param [Connection] conn
|
22
21
|
# an instance of Oedipus::Connection for querying
|
23
22
|
def initialize(name, conn)
|
24
|
-
@name
|
25
|
-
@conn
|
26
|
-
@
|
27
|
-
@builder = QueryBuilder.new(name, conn)
|
23
|
+
@name = name.to_sym
|
24
|
+
@conn = conn
|
25
|
+
@builder = QueryBuilder.new(name)
|
28
26
|
end
|
29
27
|
|
30
28
|
# Insert the record with the ID +id+.
|
31
29
|
#
|
32
30
|
# @example
|
33
|
-
# index.insert(42, :
|
31
|
+
# index.insert(42, title: "example", views: 22)
|
34
32
|
#
|
35
33
|
# @param [Integer] id
|
36
34
|
# the unique ID of the document in the index
|
@@ -38,66 +36,211 @@ module Oedipus
|
|
38
36
|
# @param [Hash] hash
|
39
37
|
# a symbol-keyed hash of data to insert
|
40
38
|
#
|
41
|
-
# @return [
|
42
|
-
#
|
39
|
+
# @return [Fixnum]
|
40
|
+
# the number of rows inserted (currently always 1)
|
43
41
|
def insert(id, hash)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
@conn.execute(@builder.insert(id, hash))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Update the record with the ID +id+.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# index.update(42, views: 25)
|
49
|
+
#
|
50
|
+
# @param [Integer] id
|
51
|
+
# the unique ID of the document in the index
|
52
|
+
#
|
53
|
+
# @param [Hash] hash
|
54
|
+
# a symbol-keyed hash of data to set
|
55
|
+
#
|
56
|
+
# @return [Fixnum]
|
57
|
+
# the number of rows updated (1 or 0)
|
58
|
+
def update(id, hash)
|
59
|
+
@conn.execute(@builder.update(id, hash))
|
50
60
|
end
|
51
61
|
|
62
|
+
# Completely replace the record with the ID +id+.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# index.replace(42, title: "New title", views: 25)
|
66
|
+
#
|
67
|
+
# @param [Integer] id
|
68
|
+
# the unique ID of the document in the index
|
69
|
+
#
|
70
|
+
# @param [Hash] hash
|
71
|
+
# a symbol-keyed hash of data to insert
|
72
|
+
#
|
73
|
+
# @return [Fixnum]
|
74
|
+
# the number of rows inserted (currentl always 1)
|
75
|
+
def replace(id, hash)
|
76
|
+
@conn.execute(@builder.replace(id, hash))
|
77
|
+
end
|
78
|
+
|
79
|
+
# Fetch a single document by its ID.
|
80
|
+
#
|
81
|
+
# Returns the Hash of attributes if found, otherwise nil.
|
82
|
+
#
|
83
|
+
# @param [Fixnum] id
|
84
|
+
# the ID of the document
|
85
|
+
#
|
86
|
+
# @return [Hash]
|
87
|
+
# the attributes of the record
|
88
|
+
def fetch(id)
|
89
|
+
search(id: id)[:records].first
|
90
|
+
end
|
91
|
+
|
92
|
+
# Perform a search on the index.
|
93
|
+
#
|
94
|
+
# Either one or two arguments may be passed, with either one being mutually
|
95
|
+
# optional.
|
96
|
+
#
|
97
|
+
# @example Fulltext search
|
98
|
+
# index.search("cats AND dogs")
|
99
|
+
#
|
100
|
+
# @example Fulltext search with attribute filters
|
101
|
+
# index.search("cats AND dogs", author_id: 57)
|
102
|
+
#
|
103
|
+
# @example Attribute search only
|
104
|
+
# index.search(author_id: 57)
|
105
|
+
#
|
106
|
+
# @param [String] query
|
107
|
+
# a fulltext query
|
108
|
+
#
|
109
|
+
# @param [Hash] filters
|
110
|
+
# attribute filters, limits, sorting and other options
|
111
|
+
#
|
112
|
+
# @return [Hash]
|
113
|
+
# a Hash containing meta data, with the records in :records
|
52
114
|
def search(*args)
|
53
|
-
|
115
|
+
multi_search(_main_: args)[:_main_]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Perform a faceted search on the index, using a base query and one or more facets.
|
119
|
+
#
|
120
|
+
# The base query is inherited by each facet, which may override (or refine) the
|
121
|
+
# query.
|
122
|
+
#
|
123
|
+
# The results returned include a :facets key, containing the results for each facet.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# index.faceted_search(
|
127
|
+
# "cats | dogs",
|
128
|
+
# category_id: 7,
|
129
|
+
# facets: {
|
130
|
+
# popular: {views: Oedipus.gt(150)},
|
131
|
+
# recent: {published_at: Oedipus.gt(Time.now.to_i - 7 * 86400)}
|
132
|
+
# }
|
133
|
+
# )
|
134
|
+
#
|
135
|
+
# @param [String] fulltext_query
|
136
|
+
# a fulltext query to search on, optional
|
137
|
+
#
|
138
|
+
# @param [Hash] options
|
139
|
+
# attribute filters and facets in a sub-hash
|
140
|
+
#
|
141
|
+
# @return [Hash]
|
142
|
+
# a Hash whose top-level result set is for the main query and which
|
143
|
+
# contains a :facets element with keys mapping 1:1 for all facets
|
144
|
+
def faceted_search(*args)
|
145
|
+
query, options = extract_query_data(args)
|
146
|
+
main_query = [query, options.reject { |k, _| k == :facets }]
|
147
|
+
facets = merge_queries(main_query, options.fetch(:facets, {}))
|
54
148
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
149
|
+
{ facets: {} }.tap do |results|
|
150
|
+
multi_search({ _main_: main_query }.merge(facets)).each do |k, v|
|
151
|
+
k == :_main_ ? results.merge!(v) : results[:facets].merge!(k => v)
|
152
|
+
end
|
59
153
|
end
|
154
|
+
end
|
60
155
|
|
61
|
-
|
62
|
-
|
156
|
+
# Perform a a batch search on the index.
|
157
|
+
#
|
158
|
+
# A Hash of queries is passed, whose keys are used to collate the results in
|
159
|
+
# the return value.
|
160
|
+
#
|
161
|
+
# Each query may either by a string (fulltext search), a Hash (attribute search)
|
162
|
+
# or an array containing both. In other words, the same arguments accepted by
|
163
|
+
# the #search method.
|
164
|
+
#
|
165
|
+
# @example
|
166
|
+
# index.multi_search(
|
167
|
+
# cat_results: ["cats", { author_id: 57 }],
|
168
|
+
# dog_results: ["dogs", { author_id: 57 }]
|
169
|
+
# )
|
170
|
+
#
|
171
|
+
# @param [Hash] queries
|
172
|
+
# a hash whose keys map to queries
|
173
|
+
#
|
174
|
+
# @return [Hash]
|
175
|
+
# a Hash whose keys map 1:1 with the input Hash, each element containing the
|
176
|
+
# same results as those returned by the #search method.
|
177
|
+
def multi_search(queries)
|
178
|
+
unless queries.kind_of?(Hash)
|
179
|
+
raise ArgumentError, "Argument must be a Hash of named queries (#{queries.class} given)"
|
180
|
+
end
|
63
181
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
182
|
+
rs = @conn.multi_query(
|
183
|
+
queries.map { |key, args|
|
184
|
+
[@builder.select(*extract_query_data(args)), "SHOW META"]
|
185
|
+
}.flatten.join(";\n")
|
186
|
+
)
|
187
|
+
|
188
|
+
Hash[].tap do |result|
|
189
|
+
queries.keys.each do |key|
|
190
|
+
records, meta = rs.shift, rs.shift
|
191
|
+
result[key] = meta_to_hash(meta).tap do |r|
|
192
|
+
r[:records] = records.map { |hash|
|
193
|
+
hash.inject({}) { |o, (k, v)| o.merge!(k.to_sym => v) }
|
194
|
+
}
|
71
195
|
end
|
72
196
|
end
|
197
|
+
end
|
198
|
+
end
|
73
199
|
|
74
|
-
|
200
|
+
private
|
75
201
|
|
76
|
-
|
77
|
-
|
202
|
+
def meta_to_hash(meta)
|
203
|
+
Hash[].tap do |hash|
|
204
|
+
meta.each do |m|
|
205
|
+
n, v = m.values
|
206
|
+
case n
|
207
|
+
when "total_found", "total" then hash[n.to_sym] = v.to_i
|
208
|
+
when "time" then hash[:time] = v.to_f
|
209
|
+
when /\Adocs\[\d+\]\Z/ then (hash[:docs] ||= []).tap { |a| a << v.to_i }
|
210
|
+
when /\Ahits\[\d+\]\Z/ then (hash[:hits] ||= []).tap { |a| a << v.to_i }
|
211
|
+
when /\Akeyword\[\d+\]\Z/ then (hash[:keywords] ||= []).tap { |a| a << v }
|
212
|
+
else hash[n.to_sym] = v
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if hash.key?(:docs) && hash.key?(:hits) && hash.key?(:keywords)
|
217
|
+
hash[:docs] = Hash[(hash[:keywords]).zip(hash[:docs])]
|
218
|
+
hash[:hits] = Hash[(hash[:keywords]).zip(hash[:hits])]
|
78
219
|
end
|
79
220
|
end
|
80
221
|
end
|
81
222
|
|
82
|
-
|
223
|
+
def extract_query_data(args, default_query = "")
|
224
|
+
args = [args] unless Array === args
|
83
225
|
|
84
|
-
|
85
|
-
|
86
|
-
when Fixnum then Integer(value)
|
87
|
-
else value
|
226
|
+
unless (1..2) === args.size
|
227
|
+
raise ArgumentError, "Wrong number of query arguments (#{args.size} for 1..2)"
|
88
228
|
end
|
89
|
-
end
|
90
229
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
case row['Type']
|
96
|
-
when 'uint', 'integer' then attrs[row['Field'].to_sym] = 0
|
97
|
-
when 'string' then attrs[row['Field'].to_sym] = ""
|
98
|
-
end
|
99
|
-
end
|
230
|
+
case args[0]
|
231
|
+
when String then [args[0], args.fetch(1, {})]
|
232
|
+
when Hash then [default_query, args[0] ]
|
233
|
+
else raise ArgumentError, "Invalid query argument type #{args.first.class}"
|
100
234
|
end
|
101
235
|
end
|
236
|
+
|
237
|
+
def merge_queries(base, others)
|
238
|
+
base_query, base_filters = base
|
239
|
+
|
240
|
+
Hash[others.map { |k, q|
|
241
|
+
query, filters = extract_query_data(q, base_query)
|
242
|
+
[k, [query.gsub("%{query}", base_query), base_filters.merge(filters)]]
|
243
|
+
}]
|
244
|
+
end
|
102
245
|
end
|
103
246
|
end
|
@@ -8,32 +8,125 @@
|
|
8
8
|
##
|
9
9
|
|
10
10
|
module Oedipus
|
11
|
+
# Constructs SphinxQL queries from the internal Hash format.
|
11
12
|
class QueryBuilder
|
12
|
-
|
13
|
+
# Initialize a new QueryBuilder for +index_name+.
|
14
|
+
#
|
15
|
+
# @param [Symbol] index_name
|
16
|
+
# the name of the index being queried
|
17
|
+
def initialize(index_name)
|
13
18
|
@index_name = index_name
|
14
|
-
@conn = conn
|
15
19
|
end
|
16
20
|
|
17
|
-
|
21
|
+
# Build a SphinxQL query for the fulltext search +query+ and filters in +filters+.
|
22
|
+
#
|
23
|
+
# @param [String] query
|
24
|
+
# the fulltext query to execute (may be empty)
|
25
|
+
#
|
26
|
+
# @param [Hash] filters
|
27
|
+
# additional attribute filters and other options
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
# a SphinxQL query
|
31
|
+
def select(query, filters)
|
18
32
|
[
|
19
33
|
from,
|
20
34
|
conditions(query, filters),
|
35
|
+
order_by(filters),
|
21
36
|
limits(filters)
|
22
37
|
].join(" ")
|
23
38
|
end
|
24
39
|
|
40
|
+
# Build a SphinxQL query to insert the record identified by +id+ with the given attributes.
|
41
|
+
#
|
42
|
+
# @param [Fixnum] id
|
43
|
+
# the unique ID of the document to insert
|
44
|
+
#
|
45
|
+
# @param [Hash] attributes
|
46
|
+
# a Hash of attributes
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
# the SphinxQL to insert the record
|
50
|
+
def insert(id, attributes)
|
51
|
+
into("INSERT", id, attributes)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Build a SphinxQL query to update the record identified by +id+ with the given attributes.
|
55
|
+
#
|
56
|
+
# @param [Fixnum] id
|
57
|
+
# the unique ID of the document to update
|
58
|
+
#
|
59
|
+
# @param [Hash] attributes
|
60
|
+
# a Hash of attributes
|
61
|
+
#
|
62
|
+
# @return [String]
|
63
|
+
# the SphinxQL to update the record
|
64
|
+
def update(id, attributes)
|
65
|
+
[
|
66
|
+
"UPDATE #{@index_name} SET",
|
67
|
+
update_attributes(attributes),
|
68
|
+
"WHERE id = #{Connection.quote(id)}"
|
69
|
+
].join(" ")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Build a SphinxQL query to replace the record identified by +id+ with the given attributes.
|
73
|
+
#
|
74
|
+
# @param [Fixnum] id
|
75
|
+
# the unique ID of the document to replace
|
76
|
+
#
|
77
|
+
# @param [Hash] attributes
|
78
|
+
# a Hash of attributes
|
79
|
+
#
|
80
|
+
# @return [String]
|
81
|
+
# the SphinxQL to replace the record
|
82
|
+
def replace(id, attributes)
|
83
|
+
into("REPLACE", id, attributes)
|
84
|
+
end
|
85
|
+
|
25
86
|
private
|
26
87
|
|
27
88
|
def from
|
28
89
|
"SELECT * FROM #{@index_name}"
|
29
90
|
end
|
30
91
|
|
92
|
+
def into(type, id, attributes)
|
93
|
+
[
|
94
|
+
type,
|
95
|
+
"INTO #{@index_name}",
|
96
|
+
"(#{([:id] + attributes.keys).join(', ')})",
|
97
|
+
"VALUES",
|
98
|
+
"(#{([id] + attributes.values).map { |v| Connection.quote(v) }.join(', ')})"
|
99
|
+
].join(" ")
|
100
|
+
end
|
101
|
+
|
31
102
|
def conditions(query, filters)
|
32
103
|
exprs = []
|
33
|
-
exprs << "MATCH(#{
|
104
|
+
exprs << "MATCH(#{Connection.quote(query)})" unless query.empty?
|
105
|
+
exprs += attribute_conditions(filters)
|
34
106
|
"WHERE " << exprs.join(" AND ") if exprs.any?
|
35
107
|
end
|
36
108
|
|
109
|
+
def attribute_conditions(filters)
|
110
|
+
filters \
|
111
|
+
.reject { |k, v| [:limit, :offset, :order].include?(k.to_sym) } \
|
112
|
+
.map { |k, v| "#{k} #{Comparison.of(v)}" }
|
113
|
+
end
|
114
|
+
|
115
|
+
def update_attributes(attributes)
|
116
|
+
attributes \
|
117
|
+
.map { |k, v| "#{k} = #{Connection.quote(v)}" } \
|
118
|
+
.join(", ")
|
119
|
+
end
|
120
|
+
|
121
|
+
def order_by(filters)
|
122
|
+
return unless filters.key?(:order)
|
123
|
+
|
124
|
+
[
|
125
|
+
"ORDER BY",
|
126
|
+
Array(filters[:order]).map { |k, dir| "#{k} #{dir ? dir.to_s.upcase : 'ASC'}" }.join(", ")
|
127
|
+
].join(" ")
|
128
|
+
end
|
129
|
+
|
37
130
|
def limits(filters)
|
38
131
|
"LIMIT #{filters[:offset].to_i}, #{filters[:limit].to_i}" if filters.key?(:limit)
|
39
132
|
end
|
data/lib/oedipus/version.rb
CHANGED
data/lib/oedipus.rb
CHANGED
@@ -8,18 +8,39 @@
|
|
8
8
|
##
|
9
9
|
|
10
10
|
require "oedipus/version"
|
11
|
+
|
12
|
+
require "oedipus/oedipus"
|
13
|
+
|
14
|
+
require "oedipus/comparison"
|
15
|
+
require "oedipus/comparison/equal"
|
16
|
+
require "oedipus/comparison/not_equal"
|
17
|
+
require "oedipus/comparison/between"
|
18
|
+
require "oedipus/comparison/outside"
|
19
|
+
require "oedipus/comparison/in"
|
20
|
+
require "oedipus/comparison/not_in"
|
21
|
+
require "oedipus/comparison/gte"
|
22
|
+
require "oedipus/comparison/gt"
|
23
|
+
require "oedipus/comparison/lte"
|
24
|
+
require "oedipus/comparison/lt"
|
25
|
+
require "oedipus/comparison/not"
|
26
|
+
require "oedipus/comparison/shortcuts"
|
27
|
+
|
11
28
|
require "oedipus/query_builder"
|
29
|
+
|
30
|
+
require "oedipus/connection_error"
|
12
31
|
require "oedipus/connection"
|
32
|
+
|
13
33
|
require "oedipus/index"
|
14
|
-
require "oedipus/mysql/client"
|
15
34
|
|
16
35
|
module Oedipus
|
36
|
+
extend Comparison::Shortcuts
|
37
|
+
|
17
38
|
class << self
|
18
39
|
# Connect to Sphinx running SphinxQL.
|
19
40
|
#
|
20
41
|
# @example
|
21
42
|
# c = Oedipus.connect("localhost:9306")
|
22
|
-
# c = Oedipus.connect(:
|
43
|
+
# c = Oedipus.connect(host: "localhost", port: 9306)
|
23
44
|
#
|
24
45
|
# @param [String] server
|
25
46
|
# a 'hostname:port' string
|
@@ -31,11 +52,7 @@ module Oedipus
|
|
31
52
|
# a client connected to SphinxQL
|
32
53
|
def connect(options)
|
33
54
|
# TODO: Add pooling
|
34
|
-
Connection.new(
|
35
|
-
options.kind_of?(String) ?
|
36
|
-
Hash[ [:host, :port].zip(options.split(":")) ] :
|
37
|
-
options
|
38
|
-
)
|
55
|
+
Connection.new(options)
|
39
56
|
end
|
40
57
|
end
|
41
58
|
end
|
data/oedipus.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = ["chris@w3style.co.uk"]
|
10
10
|
s.homepage = "https://github.com/d11wtq/oedipus"
|
11
11
|
s.summary = "Sphinx 2 Search Client for Ruby"
|
12
|
-
s.description = <<-DESC.
|
12
|
+
s.description = <<-DESC.gsub(/^ {4}/m, "")
|
13
13
|
Oedipus brings full support for Sphinx 2 to Ruby:
|
14
14
|
|
15
15
|
* real-time indexes
|
@@ -25,11 +25,10 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.rubyforge_project = "oedipus"
|
26
26
|
|
27
27
|
s.files = `git ls-files`.split("\n")
|
28
|
-
s.test_files = `git ls-files --
|
29
|
-
s.
|
28
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
29
|
+
s.extensions = ["ext/oedipus/extconf.rb"]
|
30
30
|
s.require_paths = ["lib"]
|
31
31
|
|
32
|
-
s.add_runtime_dependency "ruby-mysql"
|
33
|
-
|
34
32
|
s.add_development_dependency "rspec"
|
33
|
+
s.add_development_dependency "rake-compiler"
|
35
34
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# Oedipus Sphinx 2 Search.
|
5
|
+
# Copyright © 2012 Chris Corbyn.
|
6
|
+
#
|
7
|
+
# See LICENSE file for details.
|
8
|
+
##
|
9
|
+
|
10
|
+
require "spec_helper"
|
11
|
+
|
12
|
+
describe Oedipus::Connection do
|
13
|
+
include Oedipus::TestHarness
|
14
|
+
|
15
|
+
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
16
|
+
|
17
|
+
describe "#initialize" do
|
18
|
+
context "with a hosname:port string" do
|
19
|
+
context "on successful connection" do
|
20
|
+
it "returns the connection" do
|
21
|
+
Oedipus::Connection.new(searchd_host.values.join(":")).should be_a_kind_of(Oedipus::Connection)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "on failed connection" do
|
26
|
+
it "raises an error" do
|
27
|
+
expect {
|
28
|
+
Oedipus::Connection.new("127.0.0.1:45346138")
|
29
|
+
}.to raise_error(Oedipus::ConnectionError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with an options Hash" do
|
35
|
+
context "on successful connection" do
|
36
|
+
it "returns the connection" do
|
37
|
+
Oedipus::Connection.new(searchd_host).should be_a_kind_of(Oedipus::Connection)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "on failed connection" do
|
42
|
+
it "raises an error" do
|
43
|
+
expect {
|
44
|
+
Oedipus::Connection.new(:host => "127.0.0.1", :port => 45346138)
|
45
|
+
}.to raise_error(Oedipus::ConnectionError)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#[]" do
|
52
|
+
let(:conn) { Oedipus::Connection.new(searchd_host) }
|
53
|
+
|
54
|
+
it "returns an index" do
|
55
|
+
conn[:posts_rt].should be_a_kind_of(Oedipus::Index)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|