rsolr 1.1.2 → 2.3.0

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,59 @@
1
+ module RSolr
2
+ class Document
3
+ CHILD_DOCUMENT_KEY = '_childDocuments_'.freeze
4
+
5
+ # "attrs" is a hash for setting the "doc" xml attributes
6
+ # "fields" is an array of Field objects
7
+ attr_accessor :attrs, :fields
8
+
9
+ # "doc_hash" must be a Hash/Mash object
10
+ # If a value in the "doc_hash" is an array,
11
+ # a field object is created for each value...
12
+ def initialize(doc_hash = {})
13
+ @fields = []
14
+ doc_hash.each_pair do |field, values|
15
+ add_field(field, values)
16
+ end
17
+ @attrs={}
18
+ end
19
+
20
+ # returns an array of fields that match the "name" arg
21
+ def fields_by_name(name)
22
+ @fields.select{|f|f.name==name}
23
+ end
24
+
25
+ # returns the *first* field that matches the "name" arg
26
+ def field_by_name(name)
27
+ @fields.detect{|f|f.name==name}
28
+ end
29
+
30
+ #
31
+ # Add a field value to the document. Options map directly to
32
+ # XML attributes in the Solr <field> node.
33
+ # See http://wiki.apache.org/solr/UpdateXmlMessages#head-8315b8028923d028950ff750a57ee22cbf7977c6
34
+ #
35
+ # === Example:
36
+ #
37
+ # document.add_field('title', 'A Title', :boost => 2.0)
38
+ #
39
+ def add_field(name, values, options = {})
40
+ RSolr::Array.wrap(values).each do |v|
41
+ field_attrs = { name: name }
42
+ field_attrs[:type] = DocumentField if name.to_s == CHILD_DOCUMENT_KEY
43
+
44
+ @fields << RSolr::Field.instance(options.merge(field_attrs), v)
45
+ end
46
+ end
47
+
48
+ def as_json
49
+ @fields.group_by(&:name).each_with_object({}) do |(field, values), result|
50
+ v = values.map(&:as_json)
51
+ if v.length > 1 && v.first.is_a?(Hash) && v.first.key?(:value)
52
+ v = v.first.merge(value: v.map { |single| single[:value] })
53
+ end
54
+ v = v.first if v.length == 1 && field.to_s != CHILD_DOCUMENT_KEY
55
+ result[field] = v
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,87 @@
1
+ module RSolr
2
+ class Field
3
+ def self.instance(attrs, value)
4
+ attrs = attrs.dup
5
+ field_type = attrs.delete(:type) { value.class.name }
6
+
7
+ klass = if field_type.is_a? String
8
+ class_for_field(field_type)
9
+ elsif field_type.is_a? Class
10
+ field_type
11
+ else
12
+ self
13
+ end
14
+
15
+ klass.new(attrs, value)
16
+ end
17
+
18
+ def self.class_for_field(field_type)
19
+ potential_class_name = field_type + 'Field'.freeze
20
+ search_scope = Module.nesting[1]
21
+ search_scope.const_defined?(potential_class_name, false) ? search_scope.const_get(potential_class_name) : self
22
+ end
23
+ private_class_method :class_for_field
24
+
25
+ # "attrs" is a hash for setting the "doc" xml attributes
26
+ # "value" is the text value for the node
27
+ attr_accessor :attrs, :source_value
28
+
29
+ # "attrs" must be a hash
30
+ # "value" should be something that responds to #_to_s
31
+ def initialize(attrs, source_value)
32
+ @attrs = attrs
33
+ @source_value = source_value
34
+ end
35
+
36
+ # the value of the "name" attribute
37
+ def name
38
+ attrs[:name]
39
+ end
40
+
41
+ def value
42
+ source_value
43
+ end
44
+
45
+ def as_json
46
+ if attrs[:update]
47
+ { attrs[:update] => value }
48
+ elsif attrs.any? { |k, _| k != :name }
49
+ hash = attrs.dup
50
+ hash.delete(:name)
51
+ hash.merge(value: value)
52
+ else
53
+ value
54
+ end
55
+ end
56
+ end
57
+
58
+ class DateField < Field
59
+ def value
60
+ Time.utc(source_value.year, source_value.mon, source_value.mday).iso8601
61
+ end
62
+ end
63
+
64
+ class TimeField < Field
65
+ def value
66
+ source_value.getutc.strftime('%FT%TZ')
67
+ end
68
+ end
69
+
70
+ class DateTimeField < Field
71
+ def value
72
+ source_value.to_time.getutc.iso8601
73
+ end
74
+ end
75
+
76
+ class DocumentField < Field
77
+ def value
78
+ return RSolr::Document.new(source_value) if source_value.respond_to? :each_pair
79
+
80
+ super
81
+ end
82
+
83
+ def as_json
84
+ value.as_json
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ module RSolr
2
+ class Generator
3
+
4
+ end
5
+ end
data/lib/rsolr/json.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'json'
2
+
3
+ module RSolr::JSON
4
+ class Generator < RSolr::Generator
5
+ CONTENT_TYPE = 'application/json'.freeze
6
+
7
+ def content_type
8
+ CONTENT_TYPE
9
+ end
10
+
11
+ def add data, add_attrs = {}
12
+ add_attrs ||= {}
13
+ data = RSolr::Array.wrap(data)
14
+
15
+ if add_attrs.empty? && data.none? { |doc| doc.is_a?(RSolr::Document) && !doc.attrs.empty? }
16
+ data.map do |doc|
17
+ doc = RSolr::Document.new(doc) if doc.respond_to?(:each_pair)
18
+ yield doc if block_given?
19
+ doc.as_json
20
+ end.to_json
21
+ else
22
+ i = 0
23
+ data.each_with_object({}) do |doc, hash|
24
+ doc = RSolr::Document.new(doc) if doc.respond_to?(:each_pair)
25
+ yield doc if block_given?
26
+ hash["add__UNIQUE_RSOLR_SUFFIX_#{i += 1}"] = add_attrs.merge(doc.attrs).merge(doc: doc.as_json)
27
+ end.to_json.gsub(/__UNIQUE_RSOLR_SUFFIX_\d+/, '')
28
+ end
29
+ end
30
+
31
+ # generates a commit message
32
+ def commit(opts = {})
33
+ opts ||= {}
34
+ { commit: opts }.to_json
35
+ end
36
+
37
+ # generates a optimize message
38
+ def optimize(opts = {})
39
+ opts ||= {}
40
+ { optimize: opts }.to_json
41
+ end
42
+
43
+ # generates a rollback message
44
+ def rollback
45
+ { rollback: {} }.to_json
46
+ end
47
+
48
+ # generates a delete message
49
+ # "ids" can be a single value or array of values
50
+ def delete_by_id(ids)
51
+ { delete: ids }.to_json
52
+ end
53
+
54
+ # generates a delete message
55
+ # "queries" can be a single value or an array of values
56
+ def delete_by_query(queries)
57
+ { delete: { query: queries } }.to_json
58
+ end
59
+ end
60
+ end
@@ -11,7 +11,7 @@ module RSolr::Response
11
11
  @request = request
12
12
  @response = response
13
13
  self.merge!(result)
14
- if self["response"] && self["response"]["docs"].is_a?(Array)
14
+ if self["response"] && self["response"]["docs"].is_a?(::Array)
15
15
  docs = PaginatedDocSet.new(self["response"]["docs"])
16
16
  docs.per_page = request[:params]["rows"]
17
17
  docs.page_start = request[:params]["start"]
@@ -41,7 +41,7 @@ module RSolr::Response
41
41
  end
42
42
 
43
43
  # A response module which gets mixed into the solr ["response"]["docs"] array.
44
- class PaginatedDocSet < Array
44
+ class PaginatedDocSet < ::Array
45
45
 
46
46
  attr_accessor :page_start, :per_page, :page_total
47
47
  if not (Object.const_defined?("RUBY_ENGINE") and Object::RUBY_ENGINE=='rbx')
data/lib/rsolr/uri.rb CHANGED
@@ -1,11 +1,6 @@
1
1
  require 'uri'
2
2
 
3
3
  module RSolr::Uri
4
-
5
- def create url
6
- ::URI.parse (url[-1] == '/' || URI.parse(url).query) ? url : "#{url}/"
7
- end
8
-
9
4
  # Creates a Solr based query string.
10
5
  # Keys that have arrays values are set multiple times:
11
6
  # params_to_solr(:q => 'query', :fq => ['a', 'b'])
@@ -13,13 +8,13 @@ module RSolr::Uri
13
8
  # ?q=query&fq=a&fq=b
14
9
  # @param [boolean] escape false if no URI escaping is to be performed. Default true.
15
10
  # @return [String] Solr query params as a String, suitable for use in a url
16
- def params_to_solr(params, escape = true)
11
+ def self.params_to_solr(params, escape = true)
17
12
  return URI.encode_www_form(params.reject{|k,v| k.to_s.empty? || v.to_s.empty?}) if escape
18
13
 
19
14
  # escape = false if we are here
20
15
  mapped = params.map do |k, v|
21
16
  next if v.to_s.empty?
22
- if v.class == Array
17
+ if v.class == ::Array
23
18
  params_to_solr(v.map { |x| [k, x] }, false)
24
19
  else
25
20
  "#{k}=#{v}"
@@ -27,49 +22,4 @@ module RSolr::Uri
27
22
  end
28
23
  mapped.compact.join("&")
29
24
  end
30
-
31
- # Returns a query string param pair as a string.
32
- # Both key and value are URI escaped, unless third param is false
33
- # @param [boolean] escape false if no URI escaping is to be performed. Default true.
34
- # @deprecated - used to be called from params_to_solr before 2015-02-25
35
- def build_param(k, v, escape = true)
36
- warn "[DEPRECATION] `RSolr::Uri.build_param` is deprecated. Use `URI.encode_www_form_component` or k=v instead."
37
- escape ?
38
- "#{URI.encode_www_form_component(k)}=#{URI.encode_www_form_component(v)}" :
39
- "#{k}=#{v}"
40
- end
41
-
42
- # 2015-02 Deprecated: use URI.encode_www_form_component(s)
43
- #
44
- # Performs URI escaping so that you can construct proper
45
- # query strings faster. Use this rather than the cgi.rb
46
- # version since it's faster.
47
- # (Stolen from Rack).
48
- # http://www.rubydoc.info/github/rack/rack/URI.encode_www_form_component
49
- # @deprecated
50
- def escape_query_value(s)
51
- warn "[DEPRECATION] `RSolr::Uri.escape_query_value` is deprecated. Use `URI.encode_www_form_component` instead."
52
- URI.encode_www_form_component(s)
53
- # s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/u) {
54
- # '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
55
- # }.tr(' ', '+')
56
- end
57
-
58
- # Return the bytesize of String; uses String#size under Ruby 1.8 and
59
- # String#bytesize under 1.9.
60
- # @deprecated as bytesize was only used by escape_query_value which is itself deprecated
61
- if ''.respond_to?(:bytesize)
62
- def bytesize(string)
63
- warn "[DEPRECATION] `RSolr::Uri.bytesize` is deprecated. Use String.bytesize"
64
- string.bytesize
65
- end
66
- else
67
- def bytesize(string)
68
- warn "[DEPRECATION] `RSolr::Uri.bytesize` is deprecated. Use String.size"
69
- string.size
70
- end
71
- end
72
-
73
- extend self
74
-
75
25
  end
data/lib/rsolr/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module RSolr
2
- VERSION = "1.1.2"
2
+ VERSION = "2.3.0"
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/rsolr/xml.rb CHANGED
@@ -1,102 +1,8 @@
1
- begin; require 'nokogiri'; rescue LoadError; end
2
- require 'time'
3
-
4
1
  module RSolr::Xml
5
-
6
- class Document
7
-
8
- # "attrs" is a hash for setting the "doc" xml attributes
9
- # "fields" is an array of Field objects
10
- attr_accessor :attrs, :fields
11
-
12
- # "doc_hash" must be a Hash/Mash object
13
- # If a value in the "doc_hash" is an array,
14
- # a field object is created for each value...
15
- def initialize(doc_hash = {})
16
- @fields = []
17
- doc_hash.each_pair do |field,values|
18
- # create a new field for each value (multi-valued)
19
- wrap(values).each do |v|
20
- v = format_value(v)
21
- next if v.empty?
22
- @fields << RSolr::Xml::Field.new({:name=>field}, v)
23
- end
24
- end
25
- @attrs={}
26
- end
27
-
28
- # returns an array of fields that match the "name" arg
29
- def fields_by_name(name)
30
- @fields.select{|f|f.name==name}
31
- end
32
-
33
- # returns the *first* field that matches the "name" arg
34
- def field_by_name(name)
35
- @fields.detect{|f|f.name==name}
36
- end
37
-
38
- #
39
- # Add a field value to the document. Options map directly to
40
- # XML attributes in the Solr <field> node.
41
- # See http://wiki.apache.org/solr/UpdateXmlMessages#head-8315b8028923d028950ff750a57ee22cbf7977c6
42
- #
43
- # === Example:
44
- #
45
- # document.add_field('title', 'A Title', :boost => 2.0)
46
- #
47
- def add_field(name, value, options = {})
48
- @fields << RSolr::Xml::Field.new(options.merge({:name=>name}), value)
49
- end
50
-
51
- private
52
-
53
- def format_value(v)
54
- case v
55
- when Time
56
- v.getutc.iso8601
57
- when DateTime
58
- v.to_time.getutc.iso8601
59
- when Date
60
- Time.utc(v.year, v.mon, v.mday).iso8601
61
- else
62
- v.to_s
63
- end
64
- end
65
-
66
- def wrap(object)
67
- if object.nil?
68
- []
69
- elsif object.respond_to?(:to_ary)
70
- object.to_ary || [object]
71
- elsif object.is_a? Enumerable
72
- object
73
- else
74
- [object]
75
- end
76
- end
77
- end
2
+ Document = RSolr::Document
3
+ Field = RSolr::Field
78
4
 
79
- class Field
80
-
81
- # "attrs" is a hash for setting the "doc" xml attributes
82
- # "value" is the text value for the node
83
- attr_accessor :attrs, :value
84
-
85
- # "attrs" must be a hash
86
- # "value" should be something that responds to #_to_s
87
- def initialize(attrs, value)
88
- @attrs = attrs
89
- @value = value
90
- end
91
-
92
- # the value of the "name" attribute
93
- def name
94
- @attrs[:name]
95
- end
96
-
97
- end
98
-
99
- class Generator
5
+ class Generator < RSolr::Generator
100
6
  class << self
101
7
  attr_accessor :use_nokogiri
102
8
 
@@ -110,7 +16,14 @@ module RSolr::Xml
110
16
  end
111
17
  end
112
18
  end
113
- self.use_nokogiri = (defined?(::Nokogiri::XML::Builder) and not defined?(JRuby)) ? true : false
19
+ self.use_nokogiri = defined?(::Nokogiri::XML::Builder) ? true : false
20
+
21
+ CONTENT_TYPE = 'text/xml'.freeze
22
+
23
+ def content_type
24
+ CONTENT_TYPE
25
+ end
26
+
114
27
 
115
28
  def nokogiri_build &block
116
29
  b = ::Nokogiri::XML::Builder.new do |xml|
@@ -158,17 +71,13 @@ module RSolr::Xml
158
71
  #
159
72
  def add data, add_attrs = nil, &block
160
73
  add_attrs ||= {}
161
- data = [data] unless data.is_a?(Array)
74
+ data = RSolr::Array.wrap(data)
162
75
  build do |xml|
163
76
  xml.add(add_attrs) do |add_node|
164
77
  data.each do |doc|
165
- doc = RSolr::Xml::Document.new(doc) if doc.respond_to?(:each_pair)
78
+ doc = RSolr::Document.new(doc) if doc.respond_to?(:each_pair)
166
79
  yield doc if block_given?
167
- doc_node_builder = lambda do |doc_node|
168
- doc.fields.each do |field_obj|
169
- doc_node.field field_obj.value, field_obj.attrs
170
- end
171
- end
80
+ doc_node_builder = to_xml(doc)
172
81
  self.class.use_nokogiri ? add_node.doc_(doc.attrs,&doc_node_builder) : add_node.doc(doc.attrs,&doc_node_builder)
173
82
  end
174
83
  end
@@ -195,7 +104,7 @@ module RSolr::Xml
195
104
  # generates a <delete><id>ID</id></delete> message
196
105
  # "ids" can be a single value or array of values
197
106
  def delete_by_id ids
198
- ids = [ids] unless ids.is_a?(Array)
107
+ ids = RSolr::Array.wrap(ids)
199
108
  build do |xml|
200
109
  xml.delete do |delete_node|
201
110
  ids.each do |id|
@@ -208,12 +117,34 @@ module RSolr::Xml
208
117
  # generates a <delete><query>ID</query></delete> message
209
118
  # "queries" can be a single value or an array of values
210
119
  def delete_by_query(queries)
211
- queries = [queries] unless queries.is_a?(Array)
120
+ queries = RSolr::Array.wrap(queries)
212
121
  build do |xml|
213
122
  xml.delete do |delete_node|
214
123
  queries.each { |query| delete_node.query(query) }
215
124
  end
216
125
  end
217
126
  end
127
+
128
+ private
129
+
130
+ def to_xml(doc)
131
+ lambda do |doc_node|
132
+ doc.fields.each do |field_obj|
133
+ value = field_obj.value
134
+
135
+ if field_obj.name.to_s == RSolr::Document::CHILD_DOCUMENT_KEY
136
+ child_node_builder = to_xml(field_obj.value)
137
+ self.class.use_nokogiri ? doc_node.doc_(&child_node_builder) : doc_node.doc(&child_node_builder)
138
+ elsif value.is_a?(Hash) && value.length == 1 && field_obj.attrs[:update].nil?
139
+ update_attr, real_value = value.first
140
+ doc_node.field real_value, field_obj.attrs.merge(update: update_attr)
141
+ elsif value.nil?
142
+ doc_node.field field_obj.value, field_obj.attrs.merge(null: true)
143
+ else
144
+ doc_node.field field_obj.value, field_obj.attrs
145
+ end
146
+ end
147
+ end
148
+ end
218
149
  end
219
150
  end