telvue-rsolr 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,136 @@
1
+ module RSolr::Error
2
+
3
+ module SolrContext
4
+
5
+ attr_accessor :request, :response
6
+
7
+ def to_s
8
+ m = "#{super.to_s}"
9
+ if response
10
+ m << " - #{response[:status]} #{Http::STATUS_CODES[response[:status].to_i]}"
11
+ details = parse_solr_error_response response[:body]
12
+ m << "\nError: #{details}\n" if details
13
+ end
14
+ p = "\nURI: #{request[:uri].to_s}"
15
+ p << "\nRequest Headers: #{request[:headers].inspect}" if request[:headers]
16
+ p << "\nRequest Data: #{request[:data].inspect}" if request[:data]
17
+ p << "\n"
18
+ p << "\nBacktrace: " + self.backtrace[0..10].join("\n")
19
+ m << p
20
+ m
21
+ end
22
+
23
+ protected
24
+
25
+ def parse_solr_error_response body
26
+ begin
27
+ if body =~ /<pre>/
28
+ info = body.scan(/<pre>(.*)<\/pre>/mi)[0]
29
+ elsif body =~ /'msg'=>/
30
+ info = body.scan(/'msg'=>(.*)/)[0]
31
+ end
32
+ info = info.join if info.respond_to? :join
33
+ info ||= body # body might not contain <pre> or msg elements
34
+
35
+ partial = info.to_s.split("\n")[0..10]
36
+ partial.join("\n").gsub("&gt;", ">").gsub("&lt;", "<")
37
+ rescue
38
+ nil
39
+ end
40
+ end
41
+
42
+
43
+ end
44
+
45
+ class ConnectionRefused < ::Errno::ECONNREFUSED
46
+ end
47
+
48
+ class Http < RuntimeError
49
+
50
+ include SolrContext
51
+
52
+ # ripped right from ActionPack
53
+ # Defines the standard HTTP status codes, by integer, with their
54
+ # corresponding default message texts.
55
+ # Source: http://www.iana.org/assignments/http-status-codes
56
+ STATUS_CODES = {
57
+ 100 => "Continue",
58
+ 101 => "Switching Protocols",
59
+ 102 => "Processing",
60
+
61
+ 200 => "OK",
62
+ 201 => "Created",
63
+ 202 => "Accepted",
64
+ 203 => "Non-Authoritative Information",
65
+ 204 => "No Content",
66
+ 205 => "Reset Content",
67
+ 206 => "Partial Content",
68
+ 207 => "Multi-Status",
69
+ 226 => "IM Used",
70
+
71
+ 300 => "Multiple Choices",
72
+ 301 => "Moved Permanently",
73
+ 302 => "Found",
74
+ 303 => "See Other",
75
+ 304 => "Not Modified",
76
+ 305 => "Use Proxy",
77
+ 307 => "Temporary Redirect",
78
+
79
+ 400 => "Bad Request",
80
+ 401 => "Unauthorized",
81
+ 402 => "Payment Required",
82
+ 403 => "Forbidden",
83
+ 404 => "Not Found",
84
+ 405 => "Method Not Allowed",
85
+ 406 => "Not Acceptable",
86
+ 407 => "Proxy Authentication Required",
87
+ 408 => "Request Timeout",
88
+ 409 => "Conflict",
89
+ 410 => "Gone",
90
+ 411 => "Length Required",
91
+ 412 => "Precondition Failed",
92
+ 413 => "Request Entity Too Large",
93
+ 414 => "Request-URI Too Long",
94
+ 415 => "Unsupported Media Type",
95
+ 416 => "Requested Range Not Satisfiable",
96
+ 417 => "Expectation Failed",
97
+ 422 => "Unprocessable Entity",
98
+ 423 => "Locked",
99
+ 424 => "Failed Dependency",
100
+ 426 => "Upgrade Required",
101
+
102
+ 500 => "Internal Server Error",
103
+ 501 => "Not Implemented",
104
+ 502 => "Bad Gateway",
105
+ 503 => "Service Unavailable",
106
+ 504 => "Gateway Timeout",
107
+ 505 => "HTTP Version Not Supported",
108
+ 507 => "Insufficient Storage",
109
+ 510 => "Not Extended"
110
+ }
111
+
112
+ def initialize request, response
113
+ @request, @response = request, response
114
+ end
115
+
116
+ end
117
+
118
+ # Thrown if the :wt is :ruby
119
+ # but the body wasn't succesfully parsed/evaluated
120
+ class InvalidResponse < Http
121
+
122
+ end
123
+
124
+ # Thrown if the :wt is :ruby
125
+ # but the body wasn't succesfully parsed/evaluated
126
+ class InvalidJsonResponse < InvalidResponse
127
+
128
+ end
129
+
130
+ # Thrown if the :wt is :ruby
131
+ # but the body wasn't succesfully parsed/evaluated
132
+ class InvalidRubyResponse < InvalidResponse
133
+
134
+ end
135
+
136
+ 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
@@ -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
@@ -0,0 +1,95 @@
1
+ module RSolr::Response
2
+
3
+ def self.included(base)
4
+ unless base < Hash
5
+ raise ArgumentError, "RSolr::Response expects to included only in (sub)classes of Hash; got included in '#{base}' instead."
6
+ end
7
+ base.send(:attr_reader, :request, :response)
8
+ end
9
+
10
+ def initialize_rsolr_response(request, response, result)
11
+ @request = request
12
+ @response = response
13
+ self.merge!(result)
14
+ if self["response"] && self["response"]["docs"].is_a?(::Array)
15
+ docs = PaginatedDocSet.new(self["response"]["docs"])
16
+ docs.per_page = request[:params]["rows"]
17
+ docs.page_start = request[:params]["start"]
18
+ docs.page_total = self["response"]["numFound"].to_s.to_i
19
+ self["response"]["docs"] = docs
20
+ end
21
+ end
22
+
23
+ def with_indifferent_access
24
+ if defined?(::RSolr::HashWithIndifferentAccessWithResponse)
25
+ ::RSolr::HashWithIndifferentAccessWithResponse.new(request, response, self)
26
+ else
27
+ if defined?(ActiveSupport::HashWithIndifferentAccess)
28
+ RSolr.const_set("HashWithIndifferentAccessWithResponse", Class.new(ActiveSupport::HashWithIndifferentAccess))
29
+ RSolr::HashWithIndifferentAccessWithResponse.class_eval <<-eos
30
+ include RSolr::Response
31
+ def initialize(request, response, result)
32
+ super()
33
+ initialize_rsolr_response(request, response, result)
34
+ end
35
+ eos
36
+ ::RSolr::HashWithIndifferentAccessWithResponse.new(request, response, self)
37
+ else
38
+ raise RuntimeError, "HashWithIndifferentAccess is not currently defined"
39
+ end
40
+ end
41
+ end
42
+
43
+ # A response module which gets mixed into the solr ["response"]["docs"] array.
44
+ class PaginatedDocSet < ::Array
45
+
46
+ attr_accessor :page_start, :per_page, :page_total
47
+ if not (Object.const_defined?("RUBY_ENGINE") and Object::RUBY_ENGINE=='rbx')
48
+ alias_method(:start,:page_start)
49
+ alias_method(:start=,:page_start=)
50
+ alias_method(:total,:page_total)
51
+ alias_method(:total=,:page_total=)
52
+ end
53
+
54
+ # Returns the current page calculated from 'rows' and 'start'
55
+ def current_page
56
+ return 1 if start < 1
57
+ per_page_normalized = per_page < 1 ? 1 : per_page
58
+ @current_page ||= (start / per_page_normalized).ceil + 1
59
+ end
60
+
61
+ # Calcuates the total pages from 'numFound' and 'rows'
62
+ def total_pages
63
+ @total_pages ||= per_page > 0 ? (total / per_page.to_f).ceil : 1
64
+ end
65
+
66
+ # returns the previous page number or 1
67
+ def previous_page
68
+ @previous_page ||= (current_page > 1) ? current_page - 1 : 1
69
+ end
70
+
71
+ # returns the next page number or the last
72
+ def next_page
73
+ @next_page ||= (current_page == total_pages) ? total_pages : current_page+1
74
+ end
75
+
76
+ def has_next?
77
+ current_page < total_pages
78
+ end
79
+
80
+ def has_previous?
81
+ current_page > 1
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
88
+ class RSolr::HashWithResponse < Hash
89
+ include RSolr::Response
90
+
91
+ def initialize(request, response, result)
92
+ super()
93
+ initialize_rsolr_response(request, response, result || {})
94
+ end
95
+ end