solr4r 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # solr4r -- A Ruby client for Apache Solr #
7
7
  # #
8
- # Copyright (C) 2014-2015 Jens Wille #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
9
  # #
10
10
  # solr4r is free software: you can redistribute it and/or modify it under the #
11
11
  # terms of the GNU Affero General Public License as published by the Free #
@@ -30,6 +30,7 @@ module Solr4R
30
30
  MATCH_ALL_QUERY = '*:*'
31
31
 
32
32
  DEFAULT_SELECT_PATH = 'select'
33
+ DEFAULT_SPELL_PATH = 'spell'
33
34
  DEFAULT_MLT_PATH = 'mlt'
34
35
 
35
36
  MLT_DEFAULT_FL = '*,score'
@@ -45,7 +46,7 @@ module Solr4R
45
46
  minwl
46
47
  ], type: :mlt, qf: '$mlt.fl')
47
48
 
48
- module Query
49
+ module QueryMixin
49
50
 
50
51
  def count(
51
52
  params = {}, options = {}, path = DEFAULT_SELECT_PATH, &block)
@@ -96,7 +97,7 @@ module Solr4R
96
97
 
97
98
  end
98
99
 
99
- include Query
100
+ include QueryMixin
100
101
 
101
102
  end
102
103
 
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # solr4r -- A Ruby client for Apache Solr #
7
7
  # #
8
- # Copyright (C) 2014-2015 Jens Wille #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
9
  # #
10
10
  # solr4r is free software: you can redistribute it and/or modify it under the #
11
11
  # terms of the GNU Affero General Public License as published by the Free #
@@ -33,7 +33,7 @@ module Solr4R
33
33
 
34
34
  DEFAULT_UPDATE_PATH = 'update'
35
35
 
36
- module Update
36
+ module UpdateMixin
37
37
 
38
38
  def update(data,
39
39
  params = {}, options = {},
@@ -51,18 +51,12 @@ module Solr4R
51
51
  update(builder.add(doc, attributes), params, options, &block)
52
52
  end
53
53
 
54
- def add_batch(docs, attributes = {},
55
- params = {}, options = {}, batch_size = DEFAULT_BATCH_SIZE, &block)
56
-
57
- failed = []
58
-
59
- docs.each_slice(batch_size) { |batch|
60
- add(batch, attributes, params, options, &block).success? ||
61
- failed.concat(batch_size == 1 ? batch : add_batch(batch,
62
- attributes, params, options, batch_size / 10, &block))
63
- }
54
+ def add_batch(docs, *args, &block)
55
+ batch(*args, &block).batch(docs).flush
56
+ end
64
57
 
65
- failed
58
+ def batch(*args, &block)
59
+ Batch.new(self, *args, &block)
66
60
  end
67
61
 
68
62
  # See Builder#commit.
@@ -118,7 +112,7 @@ module Solr4R
118
112
 
119
113
  end
120
114
 
121
- include Update
115
+ include UpdateMixin
122
116
 
123
117
  end
124
118
 
data/lib/solr4r/client.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # solr4r -- A Ruby client for Apache Solr #
7
7
  # #
8
- # Copyright (C) 2014-2015 Jens Wille #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
9
  # #
10
10
  # solr4r is free software: you can redistribute it and/or modify it under the #
11
11
  # terms of the GNU Affero General Public License as published by the Free #
@@ -44,88 +44,20 @@ module Solr4R
44
44
 
45
45
  class << self
46
46
 
47
+ extend Forwardable
48
+
49
+ def_delegators Query, :escape, :query_string, :local_params_string
50
+
47
51
  def default_uri(options = {})
48
52
  DEFAULT_URI % [
49
53
  options.fetch(:host, DEFAULT_HOST),
50
54
  options.fetch(:port, DEFAULT_PORT),
51
55
  options.fetch(:path, DEFAULT_PATH),
52
- options.fetch(:core, DEFAULT_CORE)
56
+ options.fetch(:collection) {
57
+ options.fetch(:core, DEFAULT_CORE) }
53
58
  ]
54
59
  end
55
60
 
56
- def query_string(query, escape = true)
57
- case query
58
- when nil
59
- # ignore
60
- when String
61
- escape(query, escape) unless query.empty?
62
- when Array
63
- if query.last.is_a?(Hash)
64
- lp, qs = query_from_hash((query = query.dup).pop, escape)
65
- query << qs if qs
66
- end
67
-
68
- query_with_params(lp, query_string(query.join(' '), escape))
69
- when Hash
70
- query_with_params(*query_from_hash(query, escape))
71
- else
72
- type_error(query)
73
- end
74
- end
75
-
76
- def local_params_string(local_params, hash = {}, escape = true)
77
- case local_params = expand_local_params(local_params, hash.dup)
78
- when nil
79
- # ignore
80
- when String
81
- escape("{!#{local_params}}", escape) unless local_params.empty?
82
- when Array
83
- local_params_string(local_params.join(' '), {}, escape)
84
- when Hash
85
- local_params_string(local_params.map { |key, value|
86
- "#{key}=#{value =~ /\s/ ? %Q{"#{value}"} : value}" }, {}, escape)
87
- else
88
- type_error(local_params)
89
- end
90
- end
91
-
92
- def escape(string, escape = true)
93
- escape ? string.gsub('&', '%26') : string
94
- end
95
-
96
- private
97
-
98
- def query_from_hash(query, escape)
99
- local_params = query.key?(lp = :_) &&
100
- local_params_string((query = query.dup).delete(lp), {}, escape)
101
-
102
- [local_params, query_string(query.flat_map { |key, values|
103
- Array(values).map { |value| "#{key}:#{value}" } }, escape)]
104
- end
105
-
106
- def query_with_params(local_params, query_string)
107
- local_params ? local_params + query_string : query_string
108
- end
109
-
110
- def expand_local_params(local_params, hash)
111
- case type = hash[:type]
112
- when nil
113
- local_params
114
- when String, Symbol
115
- type_error(local_params, Array) unless local_params.is_a?(Array)
116
-
117
- local_params.each { |param| hash[param] = "$#{type}.#{param}" }
118
- hash
119
- else
120
- type_error(type, %w[String Symbol])
121
- end
122
- end
123
-
124
- def type_error(obj, types = %w[String Array Hash])
125
- types = Array(types).join(' or ')
126
- raise TypeError, "#{types} expected, got #{obj.class}", caller(1)
127
- end
128
-
129
61
  end
130
62
 
131
63
  def initialize(options = {})
@@ -152,6 +84,8 @@ module Solr4R
152
84
 
153
85
  def_delegators 'self.class', :query_string, :local_params_string, :escape
154
86
 
87
+ def_delegators :request, :request_line, :execute
88
+
155
89
  def json(path,
156
90
  params = {}, options = {}, &block)
157
91
 
@@ -181,7 +115,7 @@ module Solr4R
181
115
 
182
116
  def inspect
183
117
  '#<%s:0x%x @default_params=%p %s>' % [
184
- self.class, object_id, default_params, request.request_line
118
+ self.class, object_id, default_params, request_line
185
119
  ]
186
120
  end
187
121
 
@@ -191,23 +125,19 @@ module Solr4R
191
125
  self.class.default_uri(options)
192
126
  end
193
127
 
194
- def amend_options_hash(options, key, value)
195
- options.merge(key => value.merge(options.fetch(key, {})))
196
- end
197
-
198
- def amend_options_array(options, key, *value)
199
- options.merge(key => Array(options[key]) + value)
200
- end
201
-
202
128
  def send_request(path, options, &block)
203
- request.execute(path, amend_options_hash(
129
+ execute(path, amend_options_hash(
204
130
  options, :params, default_params), &block)
205
131
  end
206
132
 
133
+ def amend_options_hash(options, key, value)
134
+ options.merge(key => value.merge(options.fetch(key, {})))
135
+ end
136
+
207
137
  end
208
138
 
209
139
  end
210
140
 
211
- require_relative 'client/update'
212
- require_relative 'client/query'
213
- require_relative 'client/admin'
141
+ require_relative 'client/update_mixin'
142
+ require_relative 'client/query_mixin'
143
+ require_relative 'client/admin_mixin'
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # solr4r -- A Ruby client for Apache Solr #
7
7
  # #
8
- # Copyright (C) 2014-2015 Jens Wille #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
9
  # #
10
10
  # solr4r is free software: you can redistribute it and/or modify it under the #
11
11
  # terms of the GNU Affero General Public License as published by the Free #
@@ -33,6 +33,9 @@ module Solr4R
33
33
 
34
34
  def initialize(result, hash)
35
35
  @result, @hash = result, hash
36
+
37
+ hash.each { |key, val|
38
+ define_singleton_method(key) { val } unless respond_to?(key, true) }
36
39
  end
37
40
 
38
41
  attr_reader :result
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # solr4r -- A Ruby client for Apache Solr #
7
7
  # #
8
- # Copyright (C) 2014-2015 Jens Wille #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
9
  # #
10
10
  # solr4r is free software: you can redistribute it and/or modify it under the #
11
11
  # terms of the GNU Affero General Public License as published by the Free #
@@ -42,6 +42,13 @@ module Solr4R
42
42
  case path
43
43
  when nil
44
44
  # ignore
45
+ when Symbol
46
+ register(path.to_s, options)
47
+ when Array
48
+ path.each { |args| register(*args) }
49
+ when Hash
50
+ path.each { |_path, _options| register(_path,
51
+ _options.is_a?(Hash) ? _options : { path: _options }) }
45
52
  when String
46
53
  name, path = File.basename(path), options.fetch(:path, path).to_s
47
54
 
@@ -54,15 +61,8 @@ module Solr4R
54
61
  client.send(:send_request, path, options.merge(_options.merge(
55
62
  params: options.fetch(:params, {}).merge(_params))), &block)
56
63
  }
57
- when Symbol
58
- register(path.to_s, options)
59
- when Array
60
- path.each { |args| register(*args) }
61
- when Hash
62
- path.each { |_path, _options| register(_path,
63
- _options.is_a?(Hash) ? _options : { path: _options }) }
64
64
  else
65
- client.class.send(:type_error, path, %w[String Symbol Array Hash])
65
+ raise TypeError, "unexpected type #{path.class}"
66
66
  end
67
67
 
68
68
  self
@@ -0,0 +1,181 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # solr4r -- A Ruby client for Apache Solr #
7
+ # #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
+ # #
10
+ # solr4r is free software: you can redistribute it and/or modify it under the #
11
+ # terms of the GNU Affero General Public License as published by the Free #
12
+ # Software Foundation, either version 3 of the License, or (at your option) #
13
+ # any later version. #
14
+ # #
15
+ # solr4r is distributed in the hope that it will be useful, but WITHOUT ANY #
16
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
17
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
18
+ # more details. #
19
+ # #
20
+ # You should have received a copy of the GNU Affero General Public License #
21
+ # along with solr4r. If not, see <http://www.gnu.org/licenses/>. #
22
+ # #
23
+ ###############################################################################
24
+ #++
25
+
26
+ require 'time'
27
+
28
+ module Solr4R
29
+
30
+ class Query
31
+
32
+ LOCAL_PARAMS_KEY = :_
33
+
34
+ class << self
35
+
36
+ def query_string(*args)
37
+ new(*args).to_s
38
+ end
39
+
40
+ def local_params_string(*args)
41
+ new.local_params_string(*args)
42
+ end
43
+
44
+ def escape(string, escape = true)
45
+ escape ? string.gsub('&', '%26') : string
46
+ end
47
+
48
+ def quote(string)
49
+ string =~ /\s/ ? %Q{"#{string}"} : string
50
+ end
51
+
52
+ def convert_value(value, escape = true)
53
+ case value
54
+ when DateTime
55
+ convert_value(value.to_time, escape)
56
+ when Time
57
+ value.getutc.xmlschema.tap { |string|
58
+ string.gsub!(/:/, '\\\\\&') if escape }
59
+ when Range
60
+ '[' << [value.begin, value.end].map { |v|
61
+ convert_value(v, false) }.join(' TO ') << ']'
62
+ else
63
+ value.to_s
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ def initialize(query = '', escape = true)
70
+ @query, @escape = query, escape
71
+ end
72
+
73
+ attr_reader :query
74
+
75
+ attr_writer :escape
76
+
77
+ def escape?
78
+ @escape
79
+ end
80
+
81
+ def escape(string, escape = escape?)
82
+ self.class.escape(string, escape)
83
+ end
84
+
85
+ def quote(string)
86
+ self.class.quote(string)
87
+ end
88
+
89
+ def convert_value(*args)
90
+ self.class.convert_value(*args)
91
+ end
92
+
93
+ def to_s(escape = escape?)
94
+ query_string(query, escape)
95
+ end
96
+
97
+ def query_string(query = query(), escape = escape?)
98
+ case query
99
+ when nil
100
+ # ignore
101
+ when String
102
+ escape(query, escape) unless query.empty?
103
+ when Array
104
+ if query.last.is_a?(Hash)
105
+ query = query.dup
106
+
107
+ local_params, query_string = query_from_hash(query.pop, escape)
108
+ query << query_string if query_string
109
+ end
110
+
111
+ query_with_params(local_params, query_from_array(query, escape))
112
+ when Hash
113
+ query_with_params(*query_from_hash(query, escape))
114
+ else
115
+ type_error(query)
116
+ end
117
+ end
118
+
119
+ def local_params_string(local_params, hash = {}, escape = escape?)
120
+ case local_params = expand_local_params(local_params, hash)
121
+ when nil
122
+ # ignore
123
+ when String
124
+ escape("{!#{local_params}}", escape) unless local_params.empty?
125
+ when Array
126
+ local_params_string(local_params.join(' '), {}, escape)
127
+ when Hash
128
+ local_params_string(local_params.map { |key, value|
129
+ "#{key}=#{quote(value)}" }, {}, escape)
130
+ else
131
+ type_error(local_params)
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ def query_from_array(query, escape)
138
+ query_string(query.map { |value| convert_value(value) }.join(' '), escape)
139
+ end
140
+
141
+ def query_from_hash(query, escape)
142
+ if query.key?(LOCAL_PARAMS_KEY)
143
+ query = query.dup
144
+
145
+ local_params = local_params_string(
146
+ query.delete(LOCAL_PARAMS_KEY), {}, escape)
147
+ end
148
+
149
+ query = query.flat_map { |key, values|
150
+ block = lambda { |value| "#{key}:#{convert_value(value)}" }
151
+ values.respond_to?(:to_ary) ? values.map(&block) : block[values] }
152
+
153
+ [local_params, query_string(query, escape)]
154
+ end
155
+
156
+ def query_with_params(local_params, query_string)
157
+ local_params ? local_params + query_string : query_string
158
+ end
159
+
160
+ def expand_local_params(local_params, hash)
161
+ case type = hash[:type]
162
+ when nil
163
+ local_params
164
+ when String, Symbol
165
+ type_error(local_params) unless local_params.is_a?(Array)
166
+
167
+ hash = hash.dup
168
+ local_params.each { |param| hash[param] = "$#{type}.#{param}" }
169
+ hash
170
+ else
171
+ type_error(type)
172
+ end
173
+ end
174
+
175
+ def type_error(obj)
176
+ raise TypeError, "unexpected type #{obj.class}", caller(1)
177
+ end
178
+
179
+ end
180
+
181
+ end
data/lib/solr4r/result.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # solr4r -- A Ruby client for Apache Solr #
7
7
  # #
8
- # Copyright (C) 2014-2015 Jens Wille #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
9
  # #
10
10
  # solr4r is free software: you can redistribute it and/or modify it under the #
11
11
  # terms of the GNU Affero General Public License as published by the Free #
@@ -37,18 +37,22 @@ module Solr4R
37
37
 
38
38
  extend Forwardable
39
39
 
40
- def self.types_for(hash, mix = Nuggets::String::CamelscoreMixin)
41
- constants.map { |const|
42
- if hash.key?(const.to_s.extend(mix).underscore!)
43
- mod = const_get(const)
44
- mod if mod.is_a?(Module)
45
- end
46
- }.compact
40
+ def self.types
41
+ @types ||= constants.each_with_object({}) { |const, hash|
42
+ mod, key = const_get(const), const.to_s; next unless mod.is_a?(Module)
43
+ hash[key.extend(Nuggets::String::CamelscoreMixin).underscore!] = mod
44
+ }
47
45
  end
48
46
 
49
47
  def initialize(response, hash)
50
- types = self.class.types_for(hash); extend(*types) unless types.empty?
51
48
  @response, @hash = response, hash.extend(Nuggets::Hash::DeepFetchMixin)
49
+
50
+ self.class.types.each { |key, mod|
51
+ extend(mod) if val = hash.key?(key)
52
+
53
+ respond_to?(meth = "#{key}?", true) or
54
+ define_singleton_method(meth) { val }
55
+ }
52
56
  end
53
57
 
54
58
  attr_reader :response
@@ -73,10 +77,6 @@ module Solr4R
73
77
  to_i.zero?
74
78
  end
75
79
 
76
- def error?
77
- is_a?(Error)
78
- end
79
-
80
80
  private
81
81
 
82
82
  def _each
@@ -102,6 +102,18 @@ module Solr4R
102
102
 
103
103
  end
104
104
 
105
+ module Debug
106
+
107
+ def debug
108
+ fetch(__method__.to_s)
109
+ end
110
+
111
+ def debug_explain
112
+ debug.fetch('explain')
113
+ end
114
+
115
+ end
116
+
105
117
  module Terms
106
118
 
107
119
  def to_i
@@ -142,6 +154,30 @@ module Solr4R
142
154
 
143
155
  end
144
156
 
157
+ module Highlighting
158
+
159
+ def highlighting
160
+ fetch(__method__.to_s)
161
+ end
162
+
163
+ end
164
+
165
+ module Spellcheck
166
+
167
+ def spellcheck
168
+ fetch(__method__.to_s)
169
+ end
170
+
171
+ def spellcheck_collations
172
+ return enum_for(__method__) unless block_given?
173
+
174
+ spellcheck.fetch('collations').each_slice(2) { |_, collation|
175
+ yield collation.values_at('collationQuery', 'hits')
176
+ }
177
+ end
178
+
179
+ end
180
+
145
181
  end
146
182
 
147
183
  end
@@ -3,7 +3,7 @@ module Solr4R
3
3
  module Version
4
4
 
5
5
  MAJOR = 0
6
- MINOR = 2
6
+ MINOR = 3
7
7
  TINY = 0
8
8
 
9
9
  class << self
data/lib/solr4r.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # solr4r -- A Ruby client for Apache Solr #
7
7
  # #
8
- # Copyright (C) 2014-2015 Jens Wille #
8
+ # Copyright (C) 2014-2016 Jens Wille #
9
9
  # #
10
10
  # solr4r is free software: you can redistribute it and/or modify it under the #
11
11
  # terms of the GNU Affero General Public License as published by the Free #
@@ -41,4 +41,6 @@ require_relative 'solr4r/builder'
41
41
  require_relative 'solr4r/request'
42
42
  require_relative 'solr4r/response'
43
43
  require_relative 'solr4r/endpoints'
44
+ require_relative 'solr4r/query'
45
+ require_relative 'solr4r/batch'
44
46
  require_relative 'solr4r/client'
@@ -1,7 +1,5 @@
1
1
  describe Solr4R::Builder do
2
2
 
3
- subject { described_class.new(Solr4R::Client.new) }
4
-
5
3
  describe '#add' do
6
4
 
7
5
  example do
@@ -100,6 +98,39 @@ describe Solr4R::Builder do
100
98
  EOT
101
99
  end
102
100
 
101
+ example do
102
+ expect(subject.add(date: Date.new(1992, 03, 15))).to eq(<<-EOT)
103
+ <?xml version="1.0" encoding="UTF-8"?>
104
+ <add>
105
+ <doc>
106
+ <field name="date">1992-03-15T00:00:00Z</field>
107
+ </doc>
108
+ </add>
109
+ EOT
110
+ end
111
+
112
+ example do
113
+ expect(subject.add(time: Time.new(1992, 03, 15, 16, 23, 55, 3600))).to eq(<<-EOT)
114
+ <?xml version="1.0" encoding="UTF-8"?>
115
+ <add>
116
+ <doc>
117
+ <field name="time">1992-03-15T15:23:55Z</field>
118
+ </doc>
119
+ </add>
120
+ EOT
121
+ end
122
+
123
+ example do
124
+ expect(subject.add(datetime: DateTime.new(1992, 03, 15, 16, 23, 55, '+1'))).to eq(<<-EOT)
125
+ <?xml version="1.0" encoding="UTF-8"?>
126
+ <add>
127
+ <doc>
128
+ <field name="datetime">1992-03-15T15:23:55Z</field>
129
+ </doc>
130
+ </add>
131
+ EOT
132
+ end
133
+
103
134
  end
104
135
 
105
136
  describe '#commit' do
@@ -46,6 +46,20 @@ describe Solr4R::Client do
46
46
  ['title:foo -author%26:bar -author%26:baz', title: 'foo', '-author&' => %w[bar baz]],
47
47
  ['title:foo -author&:bar -author&:baz', { title: 'foo', '-author&' => %w[bar baz] }, false],
48
48
 
49
+ ['date:1992-03-15', date: Date.new(1992, 03, 15)],
50
+ ['time:1992-03-15T15\:23\:55Z', time: Time.new(1992, 03, 15, 16, 23, 55, 3600)],
51
+ ['datetime:1992-03-15T15\:23\:55Z', datetime: DateTime.new(1992, 03, 15, 16, 23, 55, '+1')],
52
+
53
+ ['stringrange:[a TO z]', stringrange: 'a'..'z'],
54
+ ['integerrange:[15 TO 25]', integerrange: 15..25],
55
+ ['floatrange:[-1.5 TO 2.5]', floatrange: -1.5..2.5],
56
+ ['daterange:[1992-03-15 TO 1992-04-25]',
57
+ daterange: Date.new(1992, 03, 15)..Date.new(1992, 04, 25)],
58
+ ['timerange:[1992-03-15T15:23:55Z TO 1992-04-25T15:23:55Z]',
59
+ timerange: Time.new(1992, 03, 15, 16, 23, 55, 3600)..Time.new(1992, 04, 25, 16, 23, 55, 3600)],
60
+ ['datetimerange:[1992-03-15T15:23:55Z TO 1992-04-25T15:23:55Z]',
61
+ datetimerange: DateTime.new(1992, 03, 15, 16, 23, 55, '+1')..DateTime.new(1992, 04, 25, 16, 23, 55, '+1')],
62
+
49
63
  ['{!q.op=AND}title:foo', title: 'foo', _: 'q.op=AND'],
50
64
  ['{!q.op=AND}title:foo author:bar', ['title:foo', author: 'bar', _: 'q.op=AND']],
51
65
  ['{!q.op=AND df=title}foo', ['foo', _: 'q.op=AND df=title']],