zinx 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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