mwmitchell-rsolr-ext 0.4.1 → 0.5.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.
- data/lib/core_ext.rb +5 -0
- data/lib/mash.rb +143 -0
- data/lib/rsolr-ext/request.rb +37 -2
- data/lib/rsolr-ext/response/doc_ext.rb +38 -0
- data/lib/rsolr-ext/response/facet_paginator.rb +24 -0
- data/lib/rsolr-ext/response/facetable.rb +64 -0
- data/lib/rsolr-ext/response/pageable.rb +30 -0
- data/lib/rsolr-ext/response.rb +63 -4
- data/lib/rsolr-ext.rb +2 -1
- data/rsolr-ext.gemspec +23 -15
- data/test/response_test.rb +17 -2
- metadata +12 -10
- data/lib/rsolr-ext/response/base.rb +0 -35
- data/lib/rsolr-ext/response/luke.rb +0 -53
- data/lib/rsolr-ext/response/select.rb +0 -216
- data/lib/rsolr-ext/response/update.rb +0 -13
data/lib/core_ext.rb
ADDED
data/lib/mash.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# This class has dubious semantics and we only have it so that people can write
|
2
|
+
# params[:key] instead of params['key'].
|
3
|
+
class Mash < Hash
|
4
|
+
|
5
|
+
# @param constructor<Object>
|
6
|
+
# The default value for the mash. Defaults to an empty hash.
|
7
|
+
#
|
8
|
+
# @details [Alternatives]
|
9
|
+
# If constructor is a Hash, a new mash will be created based on the keys of
|
10
|
+
# the hash and no default value will be set.
|
11
|
+
def initialize(constructor = {})
|
12
|
+
if constructor.is_a?(Hash)
|
13
|
+
super()
|
14
|
+
update(constructor)
|
15
|
+
else
|
16
|
+
super(constructor)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param key<Object> The default value for the mash. Defaults to nil.
|
21
|
+
#
|
22
|
+
# @details [Alternatives]
|
23
|
+
# If key is a Symbol and it is a key in the mash, then the default value will
|
24
|
+
# be set to the value matching the key.
|
25
|
+
def default(key = nil)
|
26
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
27
|
+
self[key]
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
34
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
35
|
+
|
36
|
+
# @param key<Object> The key to set.
|
37
|
+
# @param value<Object>
|
38
|
+
# The value to set the key to.
|
39
|
+
#
|
40
|
+
# @see Mash#convert_key
|
41
|
+
# @see Mash#convert_value
|
42
|
+
def []=(key, value)
|
43
|
+
regular_writer(convert_key(key), convert_value(value))
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param other_hash<Hash>
|
47
|
+
# A hash to update values in the mash with. The keys and the values will be
|
48
|
+
# converted to Mash format.
|
49
|
+
#
|
50
|
+
# @return <Mash> The updated mash.
|
51
|
+
def update(other_hash)
|
52
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
alias_method :merge!, :update
|
57
|
+
|
58
|
+
# @param key<Object> The key to check for. This will be run through convert_key.
|
59
|
+
#
|
60
|
+
# @return <TrueClass, FalseClass> True if the key exists in the mash.
|
61
|
+
def key?(key)
|
62
|
+
super(convert_key(key))
|
63
|
+
end
|
64
|
+
|
65
|
+
# def include? def has_key? def member?
|
66
|
+
alias_method :include?, :key?
|
67
|
+
alias_method :has_key?, :key?
|
68
|
+
alias_method :member?, :key?
|
69
|
+
|
70
|
+
# @param key<Object> The key to fetch. This will be run through convert_key.
|
71
|
+
# @param *extras<Array> Default value.
|
72
|
+
#
|
73
|
+
# @return <Object> The value at key or the default value.
|
74
|
+
def fetch(key, *extras)
|
75
|
+
super(convert_key(key), *extras)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param *indices<Array>
|
79
|
+
# The keys to retrieve values for. These will be run through +convert_key+.
|
80
|
+
#
|
81
|
+
# @return <Array> The values at each of the provided keys
|
82
|
+
def values_at(*indices)
|
83
|
+
indices.collect {|key| self[convert_key(key)]}
|
84
|
+
end
|
85
|
+
|
86
|
+
# @return <Mash> A duplicate of this mash.
|
87
|
+
def dup
|
88
|
+
Mash.new(self)
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param hash<Hash> The hash to merge with the mash.
|
92
|
+
#
|
93
|
+
# @return <Mash> A new mash with the hash values merged in.
|
94
|
+
def merge(hash)
|
95
|
+
self.dup.update(hash)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param key<Object>
|
99
|
+
# The key to delete from the mash.\
|
100
|
+
def delete(key)
|
101
|
+
super(convert_key(key))
|
102
|
+
end
|
103
|
+
|
104
|
+
# Used to provide the same interface as Hash.
|
105
|
+
#
|
106
|
+
# @return <Mash> This mash unchanged.
|
107
|
+
def stringify_keys!; self end
|
108
|
+
|
109
|
+
# @return <Hash> The mash as a Hash with string keys.
|
110
|
+
def to_hash
|
111
|
+
Hash.new(default).merge(self)
|
112
|
+
end
|
113
|
+
|
114
|
+
protected
|
115
|
+
# @param key<Object> The key to convert.
|
116
|
+
#
|
117
|
+
# @param <Object>
|
118
|
+
# The converted key. If the key was a symbol, it will be converted to a
|
119
|
+
# string.
|
120
|
+
#
|
121
|
+
# @api private
|
122
|
+
def convert_key(key)
|
123
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
124
|
+
end
|
125
|
+
|
126
|
+
# @param value<Object> The value to convert.
|
127
|
+
#
|
128
|
+
# @return <Object>
|
129
|
+
# The converted value. A Hash or an Array of hashes, will be converted to
|
130
|
+
# their Mash equivalents.
|
131
|
+
#
|
132
|
+
# @api private
|
133
|
+
def convert_value(value)
|
134
|
+
case value
|
135
|
+
when Hash
|
136
|
+
value.to_mash
|
137
|
+
when Array
|
138
|
+
value.collect { |e| convert_value(e) }
|
139
|
+
else
|
140
|
+
value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/lib/rsolr-ext/request.rb
CHANGED
@@ -1,7 +1,24 @@
|
|
1
1
|
module RSolr::Ext::Request
|
2
2
|
|
3
|
+
# A module that provides method mapping capabilities.
|
4
|
+
# The basic idea is to pass in a hash to the #map method,
|
5
|
+
# the map method then goes through a list of keys to
|
6
|
+
# be processed. Each key name can match a key in the input hash.
|
7
|
+
# If there is a match, a method by the name of "map_#{key}" is
|
8
|
+
# called with the following args: input[key], output_hash
|
9
|
+
# The method is responsible for processing the value.
|
10
|
+
# The return value from the method does nothing.
|
11
|
+
#
|
12
|
+
# For example: if the mapped params list has a name, :query,
|
13
|
+
# there should be a method like: map_query(input_value, output_hash)
|
3
14
|
module Mapable
|
4
15
|
|
16
|
+
# accepts an input hash.
|
17
|
+
# prepares a return hash by copying the input.
|
18
|
+
# runs through all of the keys in MAPPED_PARAMS.
|
19
|
+
# calls any mapper methods that match the current key in MAPPED_PARAMS.
|
20
|
+
# The mapped keys from the input hash are deleted.
|
21
|
+
# returns a new hash.
|
5
22
|
def map(input)
|
6
23
|
result = input.dup
|
7
24
|
self.class::MAPPED_PARAMS.each do |meth|
|
@@ -12,24 +29,42 @@ module RSolr::Ext::Request
|
|
12
29
|
result
|
13
30
|
end
|
14
31
|
|
15
|
-
|
32
|
+
# creates an array where the "existing_value" param is first
|
33
|
+
# and the "new_value" is the last.
|
34
|
+
# All empty/nil items are removed.
|
35
|
+
# the return result is either the result of the
|
36
|
+
# array being joined on a space, or the array itself.
|
37
|
+
# "auto_join" should be true or false.
|
38
|
+
def append_to_param(existing_value, new_value, auto_join=true)
|
16
39
|
values = [existing_value, new_value]
|
17
40
|
values.delete_if{|v|v.nil?}
|
18
|
-
values.join(' ')
|
41
|
+
auto_join ? values.join(' ') : values
|
19
42
|
end
|
20
43
|
|
21
44
|
end
|
22
45
|
|
46
|
+
# a module to help the creation of solr queries.
|
23
47
|
module Queryable
|
24
48
|
|
49
|
+
# Wraps a string around double quotes
|
25
50
|
def quote(value)
|
26
51
|
%("#{value}")
|
27
52
|
end
|
28
53
|
|
54
|
+
# builds a solr range query from a Range object
|
29
55
|
def build_range(r)
|
30
56
|
"[#{r.min} TO #{r.max}]"
|
31
57
|
end
|
32
58
|
|
59
|
+
# builds a solr query fragment
|
60
|
+
# if "quote_string" is true, the values will be quoted.
|
61
|
+
# if "value" is a string/symbol, the #to_s method is called
|
62
|
+
# if the "value" is an array, each item in the array is
|
63
|
+
# send to build_query (recursive)
|
64
|
+
# if the "value" is a Hash, a fielded query is built
|
65
|
+
# where the keys are used as the field names and
|
66
|
+
# the values are either processed as a Range or
|
67
|
+
# passed back into build_query (recursive)
|
33
68
|
def build_query(value, quote_string=false)
|
34
69
|
case value
|
35
70
|
when String,Symbol
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# module for adding helper methods to each solr response[:docs] object
|
2
|
+
module RSolr::Ext::Response::DocExt
|
3
|
+
|
4
|
+
# Helper method to check if value/multi-values exist for a given key.
|
5
|
+
# The value can be a string, or a RegExp
|
6
|
+
# Example:
|
7
|
+
# doc.has?(:location_facet)
|
8
|
+
# doc.has?(:location_facet, 'Clemons')
|
9
|
+
# doc.has?(:id, 'h009', /^u/i)
|
10
|
+
def has?(k, *values)
|
11
|
+
return if self[k].nil?
|
12
|
+
return true if self.key?(k) and values.empty?
|
13
|
+
target = self[k]
|
14
|
+
if target.is_a?(Array)
|
15
|
+
values.each do |val|
|
16
|
+
return target.any?{|tv| val.is_a?(Regexp) ? (tv =~ val) : (tv==val)}
|
17
|
+
end
|
18
|
+
else
|
19
|
+
return values.any? {|val| val.is_a?(Regexp) ? (target =~ val) : (target == val)}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# helper
|
24
|
+
# key is the name of the field
|
25
|
+
# opts is a hash with the following valid keys:
|
26
|
+
# - :sep - a string used for joining multivalued field values
|
27
|
+
# - :default - a value to return when the key doesn't exist
|
28
|
+
# if :sep is nil and the field is a multivalued field, the array is returned
|
29
|
+
def get(key, opts={:sep=>', ', :default=>nil})
|
30
|
+
if self.key? key
|
31
|
+
val = self[key]
|
32
|
+
(val.is_a?(Array) and opts[:sep]) ? val.join(opts[:sep]) : val
|
33
|
+
else
|
34
|
+
opts[:default]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class RSolr::Ext::Response::FacetPaginator
|
2
|
+
|
3
|
+
attr_reader :total, :items, :previous_offset, :next_offset
|
4
|
+
|
5
|
+
def initialize(all_facet_values, offset, limit)
|
6
|
+
offset = offset.to_s.to_i
|
7
|
+
limit = limit.to_s.to_i
|
8
|
+
total = all_facet_values.size
|
9
|
+
@items = all_facet_values.slice(0, limit-1)
|
10
|
+
@has_next = total == limit
|
11
|
+
@has_previous = offset > 0
|
12
|
+
@next_offset = offset + (limit-1)
|
13
|
+
@previous_offset = offset - (limit-1)
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_next?
|
17
|
+
@has_next
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_previous?
|
21
|
+
@has_previous
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module RSolr::Ext::Response::Facetable
|
2
|
+
|
3
|
+
# represents a facet value; which is a field value and its hit count
|
4
|
+
class FacetValue
|
5
|
+
attr_reader :value,:hits
|
6
|
+
def initialize(value,hits)
|
7
|
+
@value,@hits=value,hits
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# represents a facet; which is a field and its values
|
12
|
+
class Facet
|
13
|
+
attr_reader :field
|
14
|
+
attr_accessor :values
|
15
|
+
def initialize(field)
|
16
|
+
@field=field
|
17
|
+
@values=[]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @response.facets.each do |facet|
|
22
|
+
# facet.field
|
23
|
+
# end
|
24
|
+
# "caches" the result in the @facets instance var
|
25
|
+
def facets
|
26
|
+
# memoize!
|
27
|
+
@facets ||= (
|
28
|
+
facet_fields.inject([]) do |acc,(facet_field_name,values_and_hits_list)|
|
29
|
+
acc << facet = Facet.new(facet_field_name)
|
30
|
+
# the values_and_hits_list is an array where a value is immediately followed by it's hit count
|
31
|
+
# so we shift off an item (the value)
|
32
|
+
while value = values_and_hits_list.shift
|
33
|
+
# and then shift off the next to get the hit value
|
34
|
+
facet.values << FacetValue.new(value, values_and_hits_list.shift)
|
35
|
+
# repeat until there are no more pairs in the values_and_hits_list array
|
36
|
+
end
|
37
|
+
acc
|
38
|
+
end
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
# pass in a facet field name and get back a Facet instance
|
43
|
+
def facet_by_field_name(name)
|
44
|
+
@facets_by_field_name ||= {}
|
45
|
+
@facets_by_field_name[name] ||= (
|
46
|
+
facets.detect{|facet|facet.field.to_s == name.to_s}
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def facet_counts
|
51
|
+
@facet_counts ||= self[:facet_counts] || {}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the hash of all the facet_fields (ie: {'instock_b' => ['true', 123, 'false', 20]}
|
55
|
+
def facet_fields
|
56
|
+
@facet_fields ||= facet_counts[:facet_fields] || {}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns all of the facet queries
|
60
|
+
def facet_queries
|
61
|
+
@facet_queries ||= facet_counts[:facet_queries] || {}
|
62
|
+
end
|
63
|
+
|
64
|
+
end # end Facets
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RSolr::Ext::Response::Pageable
|
2
|
+
|
3
|
+
attr_accessor :start, :per_page, :total
|
4
|
+
|
5
|
+
# Returns the current page calculated from 'rows' and 'start'
|
6
|
+
# WillPaginate hook
|
7
|
+
def current_page
|
8
|
+
return 1 if start < 1
|
9
|
+
@current_page ||= (start / per_page).ceil + 1
|
10
|
+
end
|
11
|
+
|
12
|
+
# Calcuates the total pages from 'numFound' and 'rows'
|
13
|
+
# WillPaginate hook
|
14
|
+
def total_pages
|
15
|
+
@total_pages ||= per_page > 0 ? (total / per_page.to_f).ceil : 1
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns the previous page number or 1
|
19
|
+
# WillPaginate hook
|
20
|
+
def previous_page
|
21
|
+
@previous_page ||= (current_page > 1) ? current_page - 1 : 1
|
22
|
+
end
|
23
|
+
|
24
|
+
# returns the next page number or the last
|
25
|
+
# WillPaginate hook
|
26
|
+
def next_page
|
27
|
+
@next_page ||= (current_page < total_pages) ? current_page + 1 : total_pages
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/lib/rsolr-ext/response.rb
CHANGED
@@ -1,8 +1,67 @@
|
|
1
1
|
module RSolr::Ext::Response
|
2
2
|
|
3
|
-
autoload :
|
4
|
-
autoload :
|
5
|
-
autoload :
|
6
|
-
|
3
|
+
autoload :Facetable, 'rsolr-ext/response/facetable'
|
4
|
+
autoload :Pageable, 'rsolr-ext/response/pageable'
|
5
|
+
autoload :DocExt, 'rsolr-ext/response/doc_ext'
|
6
|
+
|
7
|
+
class Base < Mash
|
8
|
+
|
9
|
+
attr_reader :raw_response
|
10
|
+
|
11
|
+
def initialize(raw_response)
|
12
|
+
@raw_response = raw_response
|
13
|
+
super(raw_response)
|
14
|
+
RSolr::Ext::HashMethodizer.methodize!(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ok?
|
18
|
+
response_header.status == 0
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
class Standard < Base
|
25
|
+
|
26
|
+
include Facetable
|
27
|
+
|
28
|
+
def initialize(*a)
|
29
|
+
super
|
30
|
+
activate_pagination!
|
31
|
+
end
|
32
|
+
|
33
|
+
def activate_pagination!
|
34
|
+
response.docs.each{ |d| d.extend DocExt }
|
35
|
+
d = response.docs
|
36
|
+
d.extend Pageable
|
37
|
+
d.start = response_header.params[:start].to_s.to_i
|
38
|
+
d.per_page = response_header.params[:rows].to_s.to_i
|
39
|
+
d.total = d.size
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class Dismax < Standard
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
class RSolr::Ext::Response::Luke < Base
|
50
|
+
|
51
|
+
# Returns an array of fields from the index
|
52
|
+
# An optional rule can be used for "grepping" field names:
|
53
|
+
# field_list(/_facet$/)
|
54
|
+
def field_list(rule=nil)
|
55
|
+
fetch(:fields).select do |k,v|
|
56
|
+
rule ? k =~ rule : true
|
57
|
+
end.collect{|k,v|k}
|
58
|
+
end
|
59
|
+
|
60
|
+
end# end Luke
|
61
|
+
|
62
|
+
# Update
|
63
|
+
class Update < Base
|
64
|
+
|
65
|
+
end
|
7
66
|
|
8
67
|
end
|
data/lib/rsolr-ext.rb
CHANGED
data/rsolr-ext.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "rsolr-ext"
|
3
|
-
s.version = "0.
|
4
|
-
s.date = "2009-03-
|
3
|
+
s.version = "0.5.0"
|
4
|
+
s.date = "2009-03-17"
|
5
5
|
s.summary = "An extension lib for RSolr"
|
6
6
|
s.email = "goodieboy@gmail.com"
|
7
7
|
s.homepage = "http://github.com/mwmitchell/rsolr_ext"
|
@@ -9,19 +9,27 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.has_rdoc = true
|
10
10
|
s.authors = ["Matt Mitchell"]
|
11
11
|
s.files = [
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
12
|
+
|
13
|
+
"lib/core_ext.rb",
|
14
|
+
"lib/mash.rb",
|
15
|
+
|
16
|
+
"lib/rsolr-ext/hash_methodizer",
|
17
|
+
|
18
|
+
"lib/rsolr-ext/request/dismax.rb",
|
19
|
+
"lib/rsolr-ext/request/standard.rb",
|
20
|
+
"lib/rsolr-ext/request.rb",
|
21
|
+
|
22
|
+
"lib/rsolr-ext/response/doc_ext.rb",
|
23
|
+
"lib/rsolr-ext/response/facet_paginator.rb",
|
24
|
+
"lib/rsolr-ext/response/facetable.rb",
|
25
|
+
"lib/rsolr-ext/response/pageable.rb",
|
26
|
+
"lib/rsolr-ext/response.rb",
|
27
|
+
|
28
|
+
"lib/rsolr-ext.rb",
|
29
|
+
|
30
|
+
"LICENSE",
|
31
|
+
"README.rdoc",
|
32
|
+
"rsolr-ext.gemspec"
|
25
33
|
]
|
26
34
|
s.test_files = ['test/request_test.rb', 'test/response_test.rb', 'test/test_unit_test_case.rb', 'test/helper.rb']
|
27
35
|
s.extra_rdoc_files = %w(LICENSE README.rdoc)
|
data/test/response_test.rb
CHANGED
@@ -4,10 +4,25 @@ require 'helper'
|
|
4
4
|
|
5
5
|
class RSolrExtResponseTest < Test::Unit::TestCase
|
6
6
|
|
7
|
-
test 'base response
|
7
|
+
test 'base response class' do
|
8
8
|
raw_response = eval(mock_query_response)
|
9
|
-
r = RSolr::Ext::Response::Base.
|
9
|
+
r = RSolr::Ext::Response::Base.new(raw_response)
|
10
10
|
assert r.ok?
|
11
11
|
end
|
12
12
|
|
13
|
+
test 'standard response class' do
|
14
|
+
raw_response = eval(mock_query_response)
|
15
|
+
r = RSolr::Ext::Response::Standard.new(raw_response)
|
16
|
+
assert r.ok?
|
17
|
+
assert_equal 10, r.response.docs.size
|
18
|
+
assert_equal 'EXPLICIT', r.response_header.params.echo_params
|
19
|
+
assert_equal r['responseHeader'], r.response_header
|
20
|
+
assert_equal r[:responseHeader], r.response_header
|
21
|
+
assert_equal 1, r.response.docs.previous_page
|
22
|
+
assert_equal 1, r.response.docs.next_page
|
23
|
+
#
|
24
|
+
assert r.response.docs.kind_of?(RSolr::Ext::Response::Pageable)
|
25
|
+
assert r.kind_of?(RSolr::Ext::Response::Facetable)
|
26
|
+
end
|
27
|
+
|
13
28
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mwmitchell-rsolr-ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Mitchell
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-17 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -23,16 +23,18 @@ extra_rdoc_files:
|
|
23
23
|
- LICENSE
|
24
24
|
- README.rdoc
|
25
25
|
files:
|
26
|
-
- lib/
|
27
|
-
- lib/
|
28
|
-
- lib/rsolr-ext/
|
29
|
-
- lib/rsolr-ext/request/standard.rb
|
26
|
+
- lib/core_ext.rb
|
27
|
+
- lib/mash.rb
|
28
|
+
- lib/rsolr-ext/hash_methodizer
|
30
29
|
- lib/rsolr-ext/request/dismax.rb
|
30
|
+
- lib/rsolr-ext/request/standard.rb
|
31
|
+
- lib/rsolr-ext/request.rb
|
32
|
+
- lib/rsolr-ext/response/doc_ext.rb
|
33
|
+
- lib/rsolr-ext/response/facet_paginator.rb
|
34
|
+
- lib/rsolr-ext/response/facetable.rb
|
35
|
+
- lib/rsolr-ext/response/pageable.rb
|
31
36
|
- lib/rsolr-ext/response.rb
|
32
|
-
- lib/rsolr-ext
|
33
|
-
- lib/rsolr-ext/response/luke.rb
|
34
|
-
- lib/rsolr-ext/response/select.rb
|
35
|
-
- lib/rsolr-ext/response/update.rb
|
37
|
+
- lib/rsolr-ext.rb
|
36
38
|
- LICENSE
|
37
39
|
- README.rdoc
|
38
40
|
- rsolr-ext.gemspec
|
@@ -1,35 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# my_solr_hash.extend RSolrExt::Response::Base
|
3
|
-
# my_solr_hash.header
|
4
|
-
# my_solr_hash.ok?
|
5
|
-
#
|
6
|
-
module RSolr::Ext::Response::Base
|
7
|
-
|
8
|
-
def header
|
9
|
-
self[:responseHeader]
|
10
|
-
end
|
11
|
-
|
12
|
-
def params
|
13
|
-
header[:params]
|
14
|
-
end
|
15
|
-
|
16
|
-
def status
|
17
|
-
header[:status].to_i
|
18
|
-
end
|
19
|
-
|
20
|
-
def query_time
|
21
|
-
header[:QTime]
|
22
|
-
end
|
23
|
-
|
24
|
-
def ok?
|
25
|
-
self.status == 0
|
26
|
-
end
|
27
|
-
|
28
|
-
# converts to mash, then extends
|
29
|
-
def self.create(hash)
|
30
|
-
mash = hash.is_a?(Mash) ? hash : hash.to_mash
|
31
|
-
mash.extend self
|
32
|
-
mash
|
33
|
-
end
|
34
|
-
|
35
|
-
end # end Base
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module RSolr::Ext::Response::Luke
|
2
|
-
|
3
|
-
include Base
|
4
|
-
|
5
|
-
def index
|
6
|
-
self[:index]
|
7
|
-
end
|
8
|
-
|
9
|
-
def directory
|
10
|
-
index[:directory]
|
11
|
-
end
|
12
|
-
|
13
|
-
def has_deletions
|
14
|
-
index[:hasDeletions]
|
15
|
-
end
|
16
|
-
|
17
|
-
def current
|
18
|
-
index[:current]
|
19
|
-
end
|
20
|
-
|
21
|
-
def max_doc
|
22
|
-
index[:max_doc]
|
23
|
-
end
|
24
|
-
|
25
|
-
def num_docs
|
26
|
-
index[:numDocs]
|
27
|
-
end
|
28
|
-
|
29
|
-
def version
|
30
|
-
index[:version]
|
31
|
-
end
|
32
|
-
|
33
|
-
alias :has_deletions? :has_deletions
|
34
|
-
alias :optimized? :optimized
|
35
|
-
alias :current? :current
|
36
|
-
|
37
|
-
# Returns an array of fields from the index
|
38
|
-
# An optional rule can be used for "grepping" field names:
|
39
|
-
# field_list(/_facet$/)
|
40
|
-
def field_list(rule=nil)
|
41
|
-
self[:fields].select do |k,v|
|
42
|
-
rule ? k =~ rule : true
|
43
|
-
end.collect{|k,v|k}
|
44
|
-
end
|
45
|
-
|
46
|
-
# converts to mash, then extends
|
47
|
-
def self.create(hash)
|
48
|
-
mash = hash.is_a?(Mash) ? hash : hash.to_mash
|
49
|
-
mash.extend self
|
50
|
-
mash
|
51
|
-
end
|
52
|
-
|
53
|
-
end# end Luke
|
@@ -1,216 +0,0 @@
|
|
1
|
-
module RSolr::Ext::Response::Select
|
2
|
-
|
3
|
-
# module for adding helper methods to each solr response[:docs] object
|
4
|
-
module DocExt
|
5
|
-
|
6
|
-
# Helper method to check if value/multi-values exist for a given key.
|
7
|
-
# The value can be a string, or a RegExp
|
8
|
-
# Example:
|
9
|
-
# doc.has?(:location_facet)
|
10
|
-
# doc.has?(:location_facet, 'Clemons')
|
11
|
-
# doc.has?(:id, 'h009', /^u/i)
|
12
|
-
def has?(k, *values)
|
13
|
-
return if self[k].nil?
|
14
|
-
return true if self.key?(k) and values.empty?
|
15
|
-
target = self[k]
|
16
|
-
if target.is_a?(Array)
|
17
|
-
values.each do |val|
|
18
|
-
return target.any?{|tv| val.is_a?(Regexp) ? (tv =~ val) : (tv==val)}
|
19
|
-
end
|
20
|
-
else
|
21
|
-
return values.any? {|val| val.is_a?(Regexp) ? (target =~ val) : (target == val)}
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# helper
|
26
|
-
# key is the name of the field
|
27
|
-
# opts is a hash with the following valid keys:
|
28
|
-
# - :sep - a string used for joining multivalued field values
|
29
|
-
# - :default - a value to return when the key doesn't exist
|
30
|
-
# if :sep is nil and the field is a multivalued field, the array is returned
|
31
|
-
def get(key, opts={:sep=>', ', :default=>nil})
|
32
|
-
if self.key? key
|
33
|
-
val = self[key]
|
34
|
-
(val.is_a?(Array) and opts[:sep]) ? val.join(opts[:sep]) : val
|
35
|
-
else
|
36
|
-
opts[:default]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
module Facets
|
43
|
-
|
44
|
-
# represents a facet value; which is a field value and its hit count
|
45
|
-
class FacetValue
|
46
|
-
attr_reader :value,:hits
|
47
|
-
def initialize(value,hits)
|
48
|
-
@value,@hits=value,hits
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# represents a facet; which is a field and its values
|
53
|
-
class Facet
|
54
|
-
attr_reader :field
|
55
|
-
attr_accessor :values
|
56
|
-
def initialize(field)
|
57
|
-
@field=field
|
58
|
-
@values=[]
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# @response.facets.each do |facet|
|
63
|
-
# facet.field
|
64
|
-
# end
|
65
|
-
# "caches" the result in the @facets instance var
|
66
|
-
def facets
|
67
|
-
# memoize!
|
68
|
-
@facets ||= (
|
69
|
-
facet_fields.inject([]) do |acc,(facet_field_name,values_and_hits_list)|
|
70
|
-
acc << facet = Facet.new(facet_field_name)
|
71
|
-
# the values_and_hits_list is an array where a value is immediately followed by it's hit count
|
72
|
-
# so we shift off an item (the value)
|
73
|
-
while value = values_and_hits_list.shift
|
74
|
-
# and then shift off the next to get the hit value
|
75
|
-
facet.values << FacetValue.new(value, values_and_hits_list.shift)
|
76
|
-
# repeat until there are no more pairs in the values_and_hits_list array
|
77
|
-
end
|
78
|
-
acc
|
79
|
-
end
|
80
|
-
)
|
81
|
-
end
|
82
|
-
|
83
|
-
# pass in a facet field name and get back a Facet instance
|
84
|
-
def facet_by_field_name(name)
|
85
|
-
@facets_by_field_name ||= {}
|
86
|
-
@facets_by_field_name[name] ||= (
|
87
|
-
facets.detect{|facet|facet.field.to_s == name.to_s}
|
88
|
-
)
|
89
|
-
end
|
90
|
-
|
91
|
-
def facet_counts
|
92
|
-
@facet_counts ||= self[:facet_counts] || {}
|
93
|
-
end
|
94
|
-
|
95
|
-
# Returns the hash of all the facet_fields (ie: {'instock_b' => ['true', 123, 'false', 20]}
|
96
|
-
def facet_fields
|
97
|
-
@facet_fields ||= facet_counts[:facet_fields] || {}
|
98
|
-
end
|
99
|
-
|
100
|
-
# Returns all of the facet queries
|
101
|
-
def facet_queries
|
102
|
-
@facet_queries ||= facet_counts[:facet_queries] || {}
|
103
|
-
end
|
104
|
-
|
105
|
-
end # end Facets
|
106
|
-
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
class FacetPaginator
|
111
|
-
|
112
|
-
attr_reader :total, :items, :previous_offset, :next_offset
|
113
|
-
|
114
|
-
def initialize(all_facet_values, offset, limit)
|
115
|
-
offset = offset.to_s.to_i
|
116
|
-
limit = limit.to_s.to_i
|
117
|
-
total = all_facet_values.size
|
118
|
-
@items = all_facet_values.slice(0, limit-1)
|
119
|
-
@has_next = total == limit
|
120
|
-
@has_previous = offset > 0
|
121
|
-
@next_offset = offset + (limit-1)
|
122
|
-
@previous_offset = offset - (limit-1)
|
123
|
-
end
|
124
|
-
|
125
|
-
def has_next?
|
126
|
-
@has_next
|
127
|
-
end
|
128
|
-
|
129
|
-
def has_previous?
|
130
|
-
@has_previous
|
131
|
-
end
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
#
|
136
|
-
#
|
137
|
-
#
|
138
|
-
class Paginator
|
139
|
-
|
140
|
-
attr_reader :start, :per_page, :total
|
141
|
-
|
142
|
-
def initialize(start, per_page, total)
|
143
|
-
@start = start.to_s.to_i
|
144
|
-
@per_page = per_page.to_s.to_i
|
145
|
-
@total = total.to_s.to_i
|
146
|
-
end
|
147
|
-
|
148
|
-
# Returns the current page calculated from 'rows' and 'start'
|
149
|
-
# WillPaginate hook
|
150
|
-
def current_page
|
151
|
-
return 1 if start < 1
|
152
|
-
@current_page ||= (start / per_page).ceil + 1
|
153
|
-
end
|
154
|
-
|
155
|
-
# Calcuates the total pages from 'numFound' and 'rows'
|
156
|
-
# WillPaginate hook
|
157
|
-
def total_pages
|
158
|
-
@total_pages ||= per_page > 0 ? (total / per_page.to_f).ceil : 1
|
159
|
-
end
|
160
|
-
|
161
|
-
# returns the previous page number or 1
|
162
|
-
# WillPaginate hook
|
163
|
-
def previous_page
|
164
|
-
@previous_page ||= (current_page > 1) ? current_page - 1 : 1
|
165
|
-
end
|
166
|
-
|
167
|
-
# returns the next page number or the last
|
168
|
-
# WillPaginate hook
|
169
|
-
def next_page
|
170
|
-
@next_page ||= (current_page < total_pages) ? current_page + 1 : total_pages
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def paginator
|
175
|
-
@paginator ||= Paginator.new(start, rows, total)
|
176
|
-
end
|
177
|
-
|
178
|
-
# The main select response class.
|
179
|
-
# Includes the top level Response::Base module
|
180
|
-
# Includes the Pagination module.
|
181
|
-
# Each solr hash doc is extended by the DocExt module.
|
182
|
-
|
183
|
-
include RSolr::Ext::Response::Base
|
184
|
-
include Facets
|
185
|
-
|
186
|
-
def response
|
187
|
-
self[:response]
|
188
|
-
end
|
189
|
-
|
190
|
-
def num_found
|
191
|
-
response[:numFound]
|
192
|
-
end
|
193
|
-
|
194
|
-
def start
|
195
|
-
response[:start]
|
196
|
-
end
|
197
|
-
|
198
|
-
def rows
|
199
|
-
params[:rows]
|
200
|
-
end
|
201
|
-
|
202
|
-
alias :total :num_found
|
203
|
-
alias :offset :start
|
204
|
-
|
205
|
-
def docs
|
206
|
-
@docs ||= response[:docs].collect{ |d| d=d.to_mash; d.extend(DocExt); d }
|
207
|
-
end
|
208
|
-
|
209
|
-
# converts to mash, then extends
|
210
|
-
def self.create(hash)
|
211
|
-
mash = hash.is_a?(Mash) ? hash : hash.to_mash
|
212
|
-
mash.extend self
|
213
|
-
mash
|
214
|
-
end
|
215
|
-
|
216
|
-
end # end Select
|