zinx 0.0.1

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.
@@ -0,0 +1,50 @@
1
+ module Sphinx
2
+ # Pack ints, floats, strings, and arrays to internal representation
3
+ # needed by Sphinx search engine.
4
+ class Request
5
+ # Initialize new request.
6
+ def initialize
7
+ @request = ''
8
+ end
9
+
10
+ # Put int(s) to request.
11
+ def put_int(*ints)
12
+ ints.each { |i| @request << [i].pack('N') }
13
+ end
14
+
15
+ # Put 64-bit int(s) to request.
16
+ def put_int64(*ints)
17
+ ints.each { |i| @request << [i].pack('q').reverse }#[i >> 32, i & ((1 << 32) - 1)].pack('NN') }
18
+ end
19
+
20
+ # Put string(s) to request (first length, then the string itself).
21
+ def put_string(*strings)
22
+ strings.each { |s| @request << [s.length].pack('N') + s }
23
+ end
24
+
25
+ # Put float(s) to request.
26
+ def put_float(*floats)
27
+ floats.each do |f|
28
+ t1 = [f].pack('f') # machine order
29
+ t2 = t1.unpack('L*').first # int in machine order
30
+ @request << [t2].pack('N')
31
+ end
32
+ end
33
+
34
+ # Put array of ints to request (first length, then the array itself)
35
+ def put_int_array(arr)
36
+ put_int arr.length, *arr
37
+ end
38
+
39
+ # Put array of 64-bit ints to request (first length, then the array itself)
40
+ def put_int64_array(arr)
41
+ put_int arr.length
42
+ put_int64(*arr)
43
+ end
44
+
45
+ # Returns the entire message
46
+ def to_s
47
+ @request
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,69 @@
1
+ module Sphinx
2
+ # Unpack internal Sphinx representation of ints, floats, strings, and arrays.
3
+ # needed by Sphinx search engine.
4
+ class Response
5
+ # Initialize new request.
6
+ def initialize(response)
7
+ @response = response
8
+ @position = 0
9
+ @size = response.length
10
+ end
11
+
12
+ # Gets current stream position.
13
+ def position
14
+ @position
15
+ end
16
+
17
+ # Gets response size.
18
+ def size
19
+ @size
20
+ end
21
+
22
+ # Returns <tt>true</tt> when response stream is out.
23
+ def eof?
24
+ @position >= @size
25
+ end
26
+
27
+ # Get int from stream.
28
+ def get_int
29
+ raise EOFError if @position + 4 > @size
30
+ value = @response[@position, 4].unpack('N*').first
31
+ @position += 4
32
+ return value
33
+ end
34
+
35
+ # Get 64-bit int from stream.
36
+ def get_int64
37
+ raise EOFError if @position + 8 > @size
38
+ hi, lo = @response[@position, 8].unpack('N*N*')
39
+ @position += 8
40
+ return (hi << 32) + lo
41
+ end
42
+
43
+ # Get array of <tt>count</tt> ints from stream.
44
+ def get_ints(count)
45
+ length = 4 * count
46
+ raise EOFError if @position + length > @size
47
+ values = @response[@position, length].unpack('N*' * count)
48
+ @position += length
49
+ return values
50
+ end
51
+
52
+ # Get string from stream.
53
+ def get_string
54
+ length = get_int
55
+ raise EOFError if @position + length > @size
56
+ value = length > 0 ? @response[@position, length] : ''
57
+ @position += length
58
+ return value
59
+ end
60
+
61
+ # Get float from stream.
62
+ def get_float
63
+ raise EOFError if @position + 4 > @size
64
+ uval = @response[@position, 4].unpack('N*').first;
65
+ @position += 4
66
+ return ([uval].pack('L')).unpack('f*').first
67
+ end
68
+ end
69
+ end
data/lib/zinx.rb ADDED
@@ -0,0 +1,274 @@
1
+ require File.dirname(__FILE__) + '/sphinx/sphinx'
2
+
3
+ module Zinx
4
+
5
+ class Client < Sphinx::Client
6
+
7
+ class << self
8
+ attr_reader :client, # Sphinx::Client instance
9
+ :query, # Query text specified by the user
10
+ :index_name, # Index name to run the query against
11
+ :multiple_queries, # Boolean if needs to run more then one query (add_query)
12
+ :field_weights, # Hash of field weights
13
+ :index_weights, # Hash of index weights
14
+ :results # Results from Sphinx
15
+
16
+ def filter(field, value, exclude = false)
17
+ @client.SetFilter(field, value.instance_of?(Array) ? value : [value], exclude)
18
+ end
19
+
20
+ def filter_range(field, min, max, exclude = false)
21
+ @client.SetFilterRange(field, min, max, exclude)
22
+ end
23
+
24
+ def filter_float_range(field, min, max, exclude = false)
25
+ @client.SetFilterFloatRange(field, min, max, exclude)
26
+ end
27
+
28
+ def sort(mode, value = '')
29
+ @client.SetSortMode(mode, value)
30
+ end
31
+
32
+ def group(mode, value)
33
+ @client.SetGroupBy(value, mode)
34
+ end
35
+
36
+ def group_distinct(field)
37
+ @client.SetGroupDistinct(field)
38
+ end
39
+
40
+ def select(value)
41
+ @client.SetSelect(value)
42
+ end
43
+
44
+ def last_error
45
+ @client.GetLastError
46
+ end
47
+
48
+ def last_warning
49
+ @client.GetLastWarning
50
+ end
51
+
52
+ def server(host, port)
53
+ @client.SetServer(host, port)
54
+ end
55
+
56
+ def limits(offset, limit, max = 0, cutoff = 0)
57
+ @client.SetLimits(offset, limit, max, cutoff)
58
+ end
59
+
60
+ def max_query_time(time)
61
+ @client.SetMaxQueryTime(time)
62
+ end
63
+
64
+ def match_mode(mode)
65
+ @client.SetMatchMode(mode)
66
+ end
67
+
68
+ def ranking_mode(mode)
69
+ @client.SetRankingMode(mode)
70
+ end
71
+
72
+ def field_weight(field, weight)
73
+ @field_weights[field] = weight
74
+ end
75
+
76
+ def field_weights(hash)
77
+ @field_weights = hash
78
+ end
79
+
80
+ def index_weight(index, weight)
81
+ @index_weights[index] = weight
82
+ end
83
+
84
+ def index_weights(hash)
85
+ @index_weights = hash
86
+ end
87
+
88
+ def id_range(min, max)
89
+ @client.SetIDRange(min, max)
90
+ end
91
+
92
+ def geo_anchor(attr_lat, attr_lng, lat, lng)
93
+ @client.Set(attr_lat, attr_lng, lat, lng)
94
+ end
95
+
96
+ def retries(count, delay = 0)
97
+ @client.SetRetries(count, delay)
98
+ end
99
+
100
+ def override(field, type, values)
101
+ @client.SetOverride(field, type, values)
102
+ end
103
+
104
+ def reset
105
+ @client.ResetFilters
106
+ @client.ResetGroupBy
107
+ @client.ResetOverrides
108
+ end
109
+
110
+ def reset_filters
111
+ @client.ResetFilters
112
+ end
113
+
114
+ def reset_groups
115
+ @client.ResetGroupBy
116
+ end
117
+
118
+ def reset_overrides
119
+ @client.ResetOverrides
120
+ end
121
+
122
+ def build_excerpts(docs, index, words, opts = {})
123
+ init
124
+ @client.BuildExcerpts(docs, index, words, opts)
125
+ end
126
+
127
+ def excerpts(docs, words, opts = {})
128
+ run if @results.empty?
129
+ @client.BuildExcerpts(docs, @index_name, words, opts)
130
+ end
131
+
132
+ def build_keywords(query, index, hits)
133
+ init
134
+ @client.BuildKeywords(query, index, hits)
135
+ end
136
+
137
+ def update(index, attrs, values, mva = false)
138
+ init
139
+ @client.UpdateAttributes(index, attrs, values, mva)
140
+ end
141
+
142
+ def results
143
+ run if @results.empty?
144
+ @results
145
+ end
146
+
147
+ # syntax sugar for results[0].matches when using only one query
148
+ # you don't even have to call 'run' before using this
149
+ def matches
150
+ run if @results.empty?
151
+ !@multiple_queries && @results.count > 0 ? @results[0].matches : []
152
+ end
153
+
154
+ # must call run before accessing search results
155
+ def run
156
+ # set the weights
157
+ @client.SetFieldWeights(@field_weights) unless @field_weights.empty?
158
+ @client.SetIndexWeights(@index_weights) unless @index_weights.empty?
159
+
160
+ # run the query
161
+ if @multiple_queries
162
+ q = @client.RunQueries
163
+ q.each do |result|
164
+ @results << Result.new(result)
165
+ end
166
+ else
167
+ q = @client.Query(@query, @index_name)
168
+ @results << Result.new(q)
169
+ end
170
+ end
171
+
172
+ # add query for multiple queries
173
+ def add_query
174
+ @multiple_queries = true
175
+ @client.AddQuery(@query, @index_name)
176
+ reset
177
+ end
178
+
179
+ # Entry point for searches
180
+ # Valid params are:
181
+ # :server => Sphinx server address (defaults to 'localhost')
182
+ # :port => Sphinx port number (defaults to 9312)
183
+ # :match_mode => Sphinx matching mode (defaults to Zinx::SPH_MATCH_EXTENDED)
184
+ # :index_name => Name of the index to search on
185
+ def search(query, params = {}, &block)
186
+ params[:query] = query
187
+ init(params)
188
+ if !block_given?
189
+ run
190
+ return @results
191
+ else
192
+ yield
193
+ end
194
+ end
195
+
196
+ def init(params = {})
197
+ @client = Client.new
198
+ @client.SetServer(params[:server] || 'localhost', params[:port] || 9312)
199
+ @client.SetMatchMode(params[:match_mode] || Zinx::Client::SPH_MATCH_EXTENDED)
200
+ @query = params[:query]
201
+ @index_name = params[:index_name] || "*"
202
+ @multiple_queries = false
203
+ @results = []
204
+ @field_weights = {}
205
+ @index_weights = {}
206
+ end
207
+ end
208
+ end
209
+
210
+ class Match
211
+ def initialize(hash)
212
+ @match = hash
213
+ end
214
+
215
+ def each(&block)
216
+ @match.each do |m|
217
+ block.call m
218
+ end
219
+ end
220
+
221
+ def method_missing(method)
222
+ if ['groupby', 'count', 'expr'].include?("#{method}")
223
+ @match["attrs"]["@#{method}"]
224
+ elsif ['id', 'weight', 'attrs'].include?("#{method}")
225
+ @match["#{method}"]
226
+ else
227
+ @match["attrs"]["#{method}"]
228
+ end
229
+ end
230
+ end
231
+
232
+ class Result
233
+ attr_reader :matches
234
+
235
+ def initialize(sphinx_hash)
236
+ @matches = []
237
+ @sphinx_hash = sphinx_hash
238
+ @sphinx_hash["matches"].each do |match|
239
+ @matches << Match.new(match)
240
+ end
241
+ end
242
+
243
+ def method_missing(method)
244
+ @sphinx_hash["#{method}"]
245
+ end
246
+ end
247
+
248
+ module Search
249
+ class << self
250
+ attr_accessor :target
251
+
252
+ def delegate(*methods)
253
+ methods.each do |method|
254
+ define_method(method) do |*args, &block|
255
+ Search.target.send(method, *args, &block)
256
+ end
257
+ private method
258
+ end
259
+ end
260
+ end
261
+
262
+ self.target = Zinx::Client
263
+
264
+ delegate :filter, :filter_range, :filter_float_range, :sort, :group, :group_distinct,
265
+ :select, :last_error, :last_warning, :server, :limits, :max_query_time, :match_mode,
266
+ :ranking_mode, :field_weight, :field_weights, :index_weight, :index_weights,
267
+ :id_range, :geo_anchor, :retries, :override, :reset, :reset_filter, :reset_groups,
268
+ :reset_overrides, :build_excerpts, :excerpts, :build_keywords, :update, :results,
269
+ :matches, :run, :add_query, :search, :init
270
+ end
271
+
272
+ end
273
+
274
+ extend Zinx::Search
data/test/test.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'test/unit'
2
+ require 'zinx'
3
+
4
+ class ZinxTest < Test::Unit::TestCase
5
+ def test_simple_search
6
+ result = Zinx::Client.search 'something'
7
+ assert result.count > 0, 'nothing returned from search'
8
+ assert_equal result.first.error, ''
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zinx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gabriel Hora
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-15 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Simple DSL for Sphinx Search Server
15
+ email: gabrielhora@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/sphinx/sphinx/client.rb
21
+ - lib/sphinx/sphinx/request.rb
22
+ - lib/sphinx/sphinx/response.rb
23
+ - lib/sphinx/sphinx.rb
24
+ - lib/zinx.rb
25
+ - test/test.rb
26
+ homepage: http://www.polyglotdba.com/zinx
27
+ licenses: []
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 1.8.17
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: Simple DSL for Sphinx Search Server
50
+ test_files:
51
+ - test/test.rb