gojee-sunspot 2.0.3 → 2.0.4
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/.gitignore +12 -0
- data/Gemfile +5 -0
- data/History.txt +252 -0
- data/LICENSE +18 -0
- data/Rakefile +13 -0
- data/TODO +13 -0
- data/lib/light_config.rb +40 -0
- data/lib/sunspot/adapters.rb +265 -0
- data/lib/sunspot/batcher.rb +62 -0
- data/lib/sunspot/class_set.rb +23 -0
- data/lib/sunspot/composite_setup.rb +202 -0
- data/lib/sunspot/configuration.rb +53 -0
- data/lib/sunspot/data_extractor.rb +50 -0
- data/lib/sunspot/dsl/adjustable.rb +47 -0
- data/lib/sunspot/dsl/field_group.rb +57 -0
- data/lib/sunspot/dsl/field_query.rb +327 -0
- data/lib/sunspot/dsl/fields.rb +103 -0
- data/lib/sunspot/dsl/fulltext.rb +243 -0
- data/lib/sunspot/dsl/function.rb +27 -0
- data/lib/sunspot/dsl/functional.rb +44 -0
- data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
- data/lib/sunspot/dsl/paginatable.rb +32 -0
- data/lib/sunspot/dsl/query_facet.rb +36 -0
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/restriction_with_near.rb +160 -0
- data/lib/sunspot/dsl/scope.rb +217 -0
- data/lib/sunspot/dsl/search.rb +30 -0
- data/lib/sunspot/dsl/standard_query.rb +123 -0
- data/lib/sunspot/dsl.rb +5 -0
- data/lib/sunspot/field.rb +193 -0
- data/lib/sunspot/field_factory.rb +129 -0
- data/lib/sunspot/indexer.rb +136 -0
- data/lib/sunspot/query/abstract_field_facet.rb +52 -0
- data/lib/sunspot/query/bbox.rb +15 -0
- data/lib/sunspot/query/boost_query.rb +24 -0
- data/lib/sunspot/query/common_query.rb +96 -0
- data/lib/sunspot/query/composite_fulltext.rb +36 -0
- data/lib/sunspot/query/connective.rb +206 -0
- data/lib/sunspot/query/date_field_facet.rb +14 -0
- data/lib/sunspot/query/dismax.rb +132 -0
- data/lib/sunspot/query/field_facet.rb +41 -0
- data/lib/sunspot/query/field_group.rb +36 -0
- data/lib/sunspot/query/filter.rb +38 -0
- data/lib/sunspot/query/function_query.rb +52 -0
- data/lib/sunspot/query/geo.rb +53 -0
- data/lib/sunspot/query/geofilt.rb +16 -0
- data/lib/sunspot/query/highlighting.rb +62 -0
- data/lib/sunspot/query/more_like_this.rb +61 -0
- data/lib/sunspot/query/more_like_this_query.rb +12 -0
- data/lib/sunspot/query/pagination.rb +42 -0
- data/lib/sunspot/query/query_facet.rb +16 -0
- data/lib/sunspot/query/restriction.rb +262 -0
- data/lib/sunspot/query/scope.rb +9 -0
- data/lib/sunspot/query/sort.rb +109 -0
- data/lib/sunspot/query/sort_composite.rb +34 -0
- data/lib/sunspot/query/standard_query.rb +16 -0
- data/lib/sunspot/query/text_field_boost.rb +17 -0
- data/lib/sunspot/query.rb +11 -0
- data/lib/sunspot/schema.rb +151 -0
- data/lib/sunspot/search/abstract_search.rb +281 -0
- data/lib/sunspot/search/date_facet.rb +35 -0
- data/lib/sunspot/search/facet_row.rb +27 -0
- data/lib/sunspot/search/field_facet.rb +88 -0
- data/lib/sunspot/search/field_group.rb +32 -0
- data/lib/sunspot/search/group.rb +50 -0
- data/lib/sunspot/search/highlight.rb +38 -0
- data/lib/sunspot/search/hit.rb +150 -0
- data/lib/sunspot/search/hit_enumerable.rb +72 -0
- data/lib/sunspot/search/more_like_this_search.rb +31 -0
- data/lib/sunspot/search/paginated_collection.rb +57 -0
- data/lib/sunspot/search/query_facet.rb +67 -0
- data/lib/sunspot/search/standard_search.rb +21 -0
- data/lib/sunspot/search.rb +9 -0
- data/lib/sunspot/session.rb +262 -0
- data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
- data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
- data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
- data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
- data/lib/sunspot/session_proxy/multicore_session_proxy.rb +67 -0
- data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
- data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
- data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
- data/lib/sunspot/session_proxy.rb +95 -0
- data/lib/sunspot/setup.rb +350 -0
- data/lib/sunspot/text_field_setup.rb +29 -0
- data/lib/sunspot/type.rb +393 -0
- data/lib/sunspot/util.rb +252 -0
- data/lib/sunspot/version.rb +3 -0
- data/lib/sunspot.rb +579 -0
- data/log/.gitignore +1 -0
- data/pkg/.gitignore +1 -0
- data/script/console +10 -0
- data/spec/api/adapters_spec.rb +33 -0
- data/spec/api/batcher_spec.rb +112 -0
- data/spec/api/binding_spec.rb +50 -0
- data/spec/api/class_set_spec.rb +24 -0
- data/spec/api/hit_enumerable_spec.rb +47 -0
- data/spec/api/indexer/attributes_spec.rb +149 -0
- data/spec/api/indexer/batch_spec.rb +72 -0
- data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
- data/spec/api/indexer/fixed_fields_spec.rb +57 -0
- data/spec/api/indexer/fulltext_spec.rb +43 -0
- data/spec/api/indexer/removal_spec.rb +53 -0
- data/spec/api/indexer/spec_helper.rb +1 -0
- data/spec/api/indexer_spec.rb +14 -0
- data/spec/api/query/advanced_manipulation_examples.rb +35 -0
- data/spec/api/query/connectives_examples.rb +189 -0
- data/spec/api/query/dsl_spec.rb +18 -0
- data/spec/api/query/dynamic_fields_examples.rb +165 -0
- data/spec/api/query/faceting_examples.rb +397 -0
- data/spec/api/query/fulltext_examples.rb +313 -0
- data/spec/api/query/function_spec.rb +79 -0
- data/spec/api/query/geo_examples.rb +68 -0
- data/spec/api/query/group_spec.rb +32 -0
- data/spec/api/query/highlighting_examples.rb +245 -0
- data/spec/api/query/more_like_this_spec.rb +140 -0
- data/spec/api/query/ordering_pagination_examples.rb +116 -0
- data/spec/api/query/scope_examples.rb +275 -0
- data/spec/api/query/spatial_examples.rb +27 -0
- data/spec/api/query/spec_helper.rb +1 -0
- data/spec/api/query/standard_spec.rb +29 -0
- data/spec/api/query/text_field_scoping_examples.rb +30 -0
- data/spec/api/query/types_spec.rb +20 -0
- data/spec/api/search/dynamic_fields_spec.rb +33 -0
- data/spec/api/search/faceting_spec.rb +360 -0
- data/spec/api/search/highlighting_spec.rb +69 -0
- data/spec/api/search/hits_spec.rb +131 -0
- data/spec/api/search/paginated_collection_spec.rb +36 -0
- data/spec/api/search/results_spec.rb +72 -0
- data/spec/api/search/search_spec.rb +23 -0
- data/spec/api/search/spec_helper.rb +1 -0
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
- data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
- data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
- data/spec/api/session_proxy/spec_helper.rb +9 -0
- data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
- data/spec/api/session_spec.rb +232 -0
- data/spec/api/spec_helper.rb +3 -0
- data/spec/api/sunspot_spec.rb +29 -0
- data/spec/ext.rb +11 -0
- data/spec/helpers/indexer_helper.rb +17 -0
- data/spec/helpers/integration_helper.rb +8 -0
- data/spec/helpers/mock_session_helper.rb +13 -0
- data/spec/helpers/query_helper.rb +26 -0
- data/spec/helpers/search_helper.rb +68 -0
- data/spec/integration/dynamic_fields_spec.rb +57 -0
- data/spec/integration/faceting_spec.rb +251 -0
- data/spec/integration/field_grouping_spec.rb +66 -0
- data/spec/integration/geospatial_spec.rb +85 -0
- data/spec/integration/highlighting_spec.rb +44 -0
- data/spec/integration/indexing_spec.rb +55 -0
- data/spec/integration/keyword_search_spec.rb +317 -0
- data/spec/integration/local_search_spec.rb +64 -0
- data/spec/integration/more_like_this_spec.rb +43 -0
- data/spec/integration/scoped_search_spec.rb +354 -0
- data/spec/integration/stored_fields_spec.rb +12 -0
- data/spec/integration/test_pagination.rb +43 -0
- data/spec/integration/unicode_spec.rb +15 -0
- data/spec/mocks/adapters.rb +32 -0
- data/spec/mocks/blog.rb +3 -0
- data/spec/mocks/comment.rb +21 -0
- data/spec/mocks/connection.rb +126 -0
- data/spec/mocks/mock_adapter.rb +30 -0
- data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
- data/spec/mocks/mock_record.rb +52 -0
- data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
- data/spec/mocks/photo.rb +11 -0
- data/spec/mocks/post.rb +86 -0
- data/spec/mocks/super_class.rb +2 -0
- data/spec/mocks/user.rb +13 -0
- data/spec/spec_helper.rb +40 -0
- data/sunspot.gemspec +42 -0
- data/tasks/rdoc.rake +27 -0
- data/tasks/schema.rake +19 -0
- data/tasks/todo.rake +4 -0
- metadata +261 -3
@@ -0,0 +1,150 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
#
|
4
|
+
# Hit objects represent the raw information returned by Solr for a single
|
5
|
+
# document. As well as the primary key and class name, hit objects give
|
6
|
+
# access to stored field values, keyword relevance score, and keyword
|
7
|
+
# highlighting.
|
8
|
+
#
|
9
|
+
class Hit
|
10
|
+
SPECIAL_KEYS = Set.new(%w(id type score)) #:nodoc:
|
11
|
+
|
12
|
+
#
|
13
|
+
# Primary key of object associated with this hit, as string.
|
14
|
+
#
|
15
|
+
attr_reader :primary_key
|
16
|
+
#
|
17
|
+
# Class name of object associated with this hit, as string.
|
18
|
+
#
|
19
|
+
attr_reader :class_name
|
20
|
+
#
|
21
|
+
# Keyword relevance score associated with this result. Nil if this hit
|
22
|
+
# is not from a keyword search.
|
23
|
+
#
|
24
|
+
attr_reader :score
|
25
|
+
#
|
26
|
+
|
27
|
+
attr_writer :result #:nodoc:
|
28
|
+
|
29
|
+
def initialize(raw_hit, highlights, search) #:nodoc:
|
30
|
+
@class_name, @primary_key = *raw_hit['id'].match(/([^ ]+) (.+)/)[1..2]
|
31
|
+
@score = raw_hit['score']
|
32
|
+
@search = search
|
33
|
+
@stored_values = raw_hit
|
34
|
+
@stored_cache = {}
|
35
|
+
@highlights = highlights
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Returns all highlights for this hit when called without parameters.
|
40
|
+
# When a field_name is provided, returns only the highlight for this field.
|
41
|
+
#
|
42
|
+
def highlights(field_name = nil)
|
43
|
+
if field_name.nil?
|
44
|
+
highlights_cache.values.flatten
|
45
|
+
else
|
46
|
+
highlights_cache[field_name.to_sym]
|
47
|
+
end || []
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Return the first highlight found for a given field, or nil if there is
|
52
|
+
# none.
|
53
|
+
#
|
54
|
+
def highlight(field_name)
|
55
|
+
highlights(field_name).first
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Retrieve stored field value. For any attribute field configured with
|
60
|
+
# :stored => true, the Hit object will contain the stored value for
|
61
|
+
# that field. The value of this field will be typecast according to the
|
62
|
+
# type of the field.
|
63
|
+
#
|
64
|
+
# ==== Parameters
|
65
|
+
#
|
66
|
+
# field_name<Symbol>::
|
67
|
+
# The name of the field for which to retrieve the stored value.
|
68
|
+
# dynamic_field_name<Symbol>::
|
69
|
+
# If you want to access a stored dynamic field, this should be the
|
70
|
+
# dynamic component of the field name.
|
71
|
+
#
|
72
|
+
def stored(field_name, dynamic_field_name = nil)
|
73
|
+
field_key =
|
74
|
+
if dynamic_field_name
|
75
|
+
[field_name.to_sym, dynamic_field_name.to_sym]
|
76
|
+
else
|
77
|
+
field_name.to_sym
|
78
|
+
end
|
79
|
+
return @stored_cache[field_key] if @stored_cache.has_key?(field_key)
|
80
|
+
@stored_cache[field_key] = stored_value(field_name, dynamic_field_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Retrieve the instance associated with this hit. This is lazy-loaded, but
|
85
|
+
# the first time it is called on any hit, all the hits for the search will
|
86
|
+
# load their instances using the adapter's #load_all method.
|
87
|
+
#
|
88
|
+
def result
|
89
|
+
return @result if defined?(@result)
|
90
|
+
@search.populate_hits
|
91
|
+
@result
|
92
|
+
end
|
93
|
+
alias_method :instance, :result
|
94
|
+
|
95
|
+
def inspect #:nodoc:
|
96
|
+
"#<Sunspot::Search::Hit:#{@class_name} #{@primary_key}>"
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Returns the instance primary key when the Hit is used to generate urls
|
101
|
+
# For example, using a search that stores the :name attribute:
|
102
|
+
#
|
103
|
+
# hits = Sunspot.search(Object) do ...
|
104
|
+
#
|
105
|
+
# hits.each do |hit|
|
106
|
+
# link_to hit.stored(:name), edit_object_path(hit)
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
def to_param
|
110
|
+
self.primary_key
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def setup
|
116
|
+
@setup ||= Sunspot::Setup.for(Util.full_const_get(@class_name))
|
117
|
+
end
|
118
|
+
|
119
|
+
def highlights_cache
|
120
|
+
@highlights_cache ||=
|
121
|
+
begin
|
122
|
+
cache = {}
|
123
|
+
if @highlights
|
124
|
+
@highlights.each_pair do |indexed_field_name, highlight_strings|
|
125
|
+
field_name = indexed_field_name.sub(/_[a-z]+$/, '').to_sym
|
126
|
+
cache[field_name] = highlight_strings.map do |highlight_string|
|
127
|
+
Highlight.new(field_name, highlight_string)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
cache
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def stored_value(field_name, dynamic_field_name)
|
136
|
+
setup.stored_fields(field_name, dynamic_field_name).each do |field|
|
137
|
+
value = @stored_values[field.indexed_name]
|
138
|
+
|
139
|
+
if Array === value
|
140
|
+
return value.map { |item| field.cast(item) }
|
141
|
+
elsif !value.nil?
|
142
|
+
return field.cast(value)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
module HitEnumerable #:nodoc:
|
4
|
+
def hits(options = {})
|
5
|
+
if options[:verify]
|
6
|
+
verified_hits
|
7
|
+
elsif solr_docs
|
8
|
+
solr_docs.map { |d| Hit.new(d, highlights_for(d), self) }
|
9
|
+
else
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def verified_hits
|
15
|
+
hits.select { |h| h.result }
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Populate the Hit objects with their instances. This is invoked the first
|
20
|
+
# time any hit has its instance requested, and all hits are loaded as a
|
21
|
+
# batch.
|
22
|
+
#
|
23
|
+
def populate_hits #:nodoc:
|
24
|
+
id_hit_hash = Hash.new { |h, k| h[k] = {} }
|
25
|
+
hits.each do |hit|
|
26
|
+
id_hit_hash[hit.class_name][hit.primary_key] = hit
|
27
|
+
end
|
28
|
+
id_hit_hash.each_pair do |class_name, hits|
|
29
|
+
ids = hits.map { |id, hit| hit.primary_key }
|
30
|
+
data_accessor = data_accessor_for(Util.full_const_get(class_name))
|
31
|
+
hits_for_class = id_hit_hash[class_name]
|
32
|
+
data_accessor.load_all(ids).each do |result|
|
33
|
+
hit = hits_for_class.delete(Adapters::InstanceAdapter.adapt(result).id.to_s)
|
34
|
+
hit.result = result
|
35
|
+
end
|
36
|
+
hits_for_class.values.each { |hit| hit.result = nil }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Convenience method to iterate over hit and result objects. Block is
|
42
|
+
# yielded a Sunspot::Server::Hit instance and a Sunspot::Server::Result
|
43
|
+
# instance.
|
44
|
+
#
|
45
|
+
# Note that this method iterates over verified hits (see #hits method
|
46
|
+
# for more information).
|
47
|
+
#
|
48
|
+
def each_hit_with_result
|
49
|
+
verified_hits.each do |hit|
|
50
|
+
yield(hit, hit.result)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Get the data accessor that will be used to load a particular class out of
|
56
|
+
# persistent storage. Data accessors can implement any methods that may be
|
57
|
+
# useful for refining how data is loaded out of storage. When building a
|
58
|
+
# search manually (e.g., using the Sunspot#new_search method), this should
|
59
|
+
# be used before calling #execute(). Use the
|
60
|
+
# Sunspot::DSL::Search#data_accessor_for method when building searches using
|
61
|
+
# the block DSL.
|
62
|
+
#
|
63
|
+
def data_accessor_for(clazz) #:nodoc:
|
64
|
+
# FIXME: This method does not belong here, but I was getting bogged
|
65
|
+
# down trying to figure out where it should go. Punted for now.
|
66
|
+
# - AL 27 Nov 2011
|
67
|
+
(@data_accessors ||= {})[clazz.name.to_sym] ||=
|
68
|
+
Adapters::DataAccessor.create(clazz)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Sunspot
|
2
|
+
#
|
3
|
+
# This class encapsulates the results of a Solr MoreLikeThis search. It provides access
|
4
|
+
# to search results, total result count, and pagination information.
|
5
|
+
# Instances of MoreLikeThis are returned by the Sunspot.more_like_this and
|
6
|
+
# Sunspot.new_more_like_this methods.
|
7
|
+
#
|
8
|
+
module Search
|
9
|
+
class MoreLikeThisSearch < AbstractSearch
|
10
|
+
def execute
|
11
|
+
if @query.more_like_this.fields.empty?
|
12
|
+
@setup.all_more_like_this_fields.each do |field|
|
13
|
+
@query.more_like_this.add_field(field)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def request_handler
|
20
|
+
super || :mlt
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# override
|
26
|
+
def dsl
|
27
|
+
DSL::MoreLikeThisQuery.new(self, @query, @setup)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
|
4
|
+
class PaginatedCollection
|
5
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval|object_id/ }
|
6
|
+
|
7
|
+
attr_reader :current_page, :per_page
|
8
|
+
attr_accessor :total_count
|
9
|
+
alias :total_entries :total_count
|
10
|
+
alias :total_entries= :total_count=
|
11
|
+
alias :limit_value :per_page
|
12
|
+
|
13
|
+
def initialize(collection, page, per_page, total)
|
14
|
+
@collection = collection
|
15
|
+
@current_page = page
|
16
|
+
@per_page = per_page
|
17
|
+
@total_count = total
|
18
|
+
end
|
19
|
+
|
20
|
+
def total_pages
|
21
|
+
(total_count.to_f / per_page).ceil
|
22
|
+
end
|
23
|
+
alias :num_pages :total_pages
|
24
|
+
|
25
|
+
def first_page?
|
26
|
+
current_page == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_page?
|
30
|
+
current_page >= total_pages
|
31
|
+
end
|
32
|
+
|
33
|
+
def previous_page
|
34
|
+
current_page > 1 ? (current_page - 1) : nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def next_page
|
38
|
+
current_page < total_pages ? (current_page + 1) : nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def out_of_bounds?
|
42
|
+
current_page > total_pages
|
43
|
+
end
|
44
|
+
|
45
|
+
def offset
|
46
|
+
(current_page - 1) * per_page
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def method_missing(method, *args, &block)
|
52
|
+
@collection.send(method, *args, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
class QueryFacet
|
4
|
+
RequestedFacet = Struct.new(:label, :boolean_phrase) #:nodoc:
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(name, search, options) #:nodoc:
|
9
|
+
@name, @search, @options = name, search, options
|
10
|
+
@requested_facets = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def rows(options = {})
|
14
|
+
@rows ||=
|
15
|
+
begin
|
16
|
+
data = @search.facet_response['facet_queries']
|
17
|
+
rows = []
|
18
|
+
minimum_count =
|
19
|
+
case
|
20
|
+
when @options[:minimum_count] then @options[:minimum_count]
|
21
|
+
when @options[:zeros] then 0
|
22
|
+
else 1
|
23
|
+
end
|
24
|
+
@requested_facets.each do |requested_facet|
|
25
|
+
count = data[requested_facet.boolean_phrase] || 0
|
26
|
+
if count >= minimum_count
|
27
|
+
rows << FacetRow.new(requested_facet.label, count, self)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
sort_rows!(rows)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_row(label, boolean_phrase) #:nodoc:
|
35
|
+
@requested_facets << RequestedFacet.new(label, boolean_phrase)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def sort_rows!(rows)
|
41
|
+
case @options[:sort] || (:count if limit)
|
42
|
+
when :count
|
43
|
+
rows.sort! { |lrow, rrow| rrow.count <=> lrow.count }
|
44
|
+
when :index
|
45
|
+
rows.sort! do |lrow, rrow|
|
46
|
+
if lrow.respond_to?(:<=>)
|
47
|
+
lrow.value <=> rrow.value
|
48
|
+
elsif lrow.respond_to?(:first) && rrow.respond_to?(:first) && lrow.first.respond_to?(:<=>)
|
49
|
+
lrow.first.value <=> rrow.first.value
|
50
|
+
else
|
51
|
+
lrow.value.to_s <=> rrow.value.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
if limit
|
56
|
+
rows.replace(rows.first(limit))
|
57
|
+
end
|
58
|
+
rows
|
59
|
+
end
|
60
|
+
|
61
|
+
def limit
|
62
|
+
return @limit if defined?(@limit)
|
63
|
+
@limit = (@options[:limit].to_i if @options[:limit].to_i > 0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Search
|
3
|
+
#
|
4
|
+
# This class encapsulates the results of a Solr search. It provides access
|
5
|
+
# to search results, total result count, facets, and pagination information.
|
6
|
+
# Instances of Search are returned by the Sunspot.search and
|
7
|
+
# Sunspot.new_search methods.
|
8
|
+
#
|
9
|
+
class StandardSearch < AbstractSearch
|
10
|
+
def request_handler
|
11
|
+
super || :select
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def dsl
|
17
|
+
DSL::Search.new(self, @setup)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
%w(abstract_search standard_search more_like_this_search query_facet field_facet
|
2
|
+
date_facet facet_row hit highlight field_group group hit_enumerable).each do |file|
|
3
|
+
require File.join(File.dirname(__FILE__), 'search', file)
|
4
|
+
end
|
5
|
+
|
6
|
+
module Sunspot
|
7
|
+
module Search
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
module Sunspot
|
2
|
+
#
|
3
|
+
# A Sunspot session encapsulates a connection to Solr and a set of
|
4
|
+
# configuration choices. Though users of Sunspot may manually instantiate
|
5
|
+
# Session objects, in the general case it's easier to use the singleton
|
6
|
+
# stored in the Sunspot module. Since the Sunspot module provides all of
|
7
|
+
# the instance methods of Session as class methods, they are not documented
|
8
|
+
# again here.
|
9
|
+
#
|
10
|
+
class Session
|
11
|
+
class <<self
|
12
|
+
attr_writer :connection_class #:nodoc:
|
13
|
+
|
14
|
+
#
|
15
|
+
# For testing purposes
|
16
|
+
#
|
17
|
+
def connection_class #:nodoc:
|
18
|
+
@connection_class ||= RSolr
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Sunspot::Configuration object for this session
|
24
|
+
#
|
25
|
+
attr_reader :config
|
26
|
+
|
27
|
+
#
|
28
|
+
# Sessions are initialized with a Sunspot configuration and a Solr
|
29
|
+
# connection. Usually you will want to stick with the default arguments
|
30
|
+
# when instantiating your own sessions.
|
31
|
+
#
|
32
|
+
def initialize(config = Configuration.build, connection = nil)
|
33
|
+
@config = config
|
34
|
+
yield(@config) if block_given?
|
35
|
+
@connection = connection
|
36
|
+
@deletes = @adds = 0
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# See Sunspot.new_search
|
41
|
+
#
|
42
|
+
def new_search(*types, &block)
|
43
|
+
types.flatten!
|
44
|
+
search = Search::StandardSearch.new(
|
45
|
+
connection,
|
46
|
+
setup_for_types(types),
|
47
|
+
Query::StandardQuery.new(types),
|
48
|
+
@config
|
49
|
+
)
|
50
|
+
search.build(&block) if block
|
51
|
+
search
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# See Sunspot.search
|
56
|
+
#
|
57
|
+
def search(*types, &block)
|
58
|
+
search = new_search(*types, &block)
|
59
|
+
search.execute
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# See Sunspot.new_more_like_this
|
64
|
+
#
|
65
|
+
def new_more_like_this(object, *types, &block)
|
66
|
+
types[0] ||= object.class
|
67
|
+
mlt = Search::MoreLikeThisSearch.new(
|
68
|
+
connection,
|
69
|
+
setup_for_types(types),
|
70
|
+
Query::MoreLikeThisQuery.new(object, types),
|
71
|
+
@config
|
72
|
+
)
|
73
|
+
mlt.build(&block) if block
|
74
|
+
mlt
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# See Sunspot.more_like_this
|
79
|
+
#
|
80
|
+
def more_like_this(object, *types, &block)
|
81
|
+
mlt = new_more_like_this(object, *types, &block)
|
82
|
+
mlt.execute
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# See Sunspot.index
|
87
|
+
#
|
88
|
+
def index(*objects)
|
89
|
+
objects.flatten!
|
90
|
+
@adds += objects.length
|
91
|
+
indexer.add(objects)
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# See Sunspot.index!
|
96
|
+
#
|
97
|
+
def index!(*objects)
|
98
|
+
index(*objects)
|
99
|
+
commit
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# See Sunspot.commit
|
104
|
+
#
|
105
|
+
def commit
|
106
|
+
@adds = @deletes = 0
|
107
|
+
connection.commit
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# See Sunspot.optimize
|
112
|
+
#
|
113
|
+
def optimize
|
114
|
+
@adds = @deletes = 0
|
115
|
+
connection.optimize
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# See Sunspot.remove
|
120
|
+
#
|
121
|
+
def remove(*objects, &block)
|
122
|
+
if block
|
123
|
+
types = objects
|
124
|
+
conjunction = Query::Connective::Conjunction.new
|
125
|
+
if types.length == 1
|
126
|
+
conjunction.add_positive_restriction(TypeField.instance, Query::Restriction::EqualTo, types.first)
|
127
|
+
else
|
128
|
+
conjunction.add_positive_restriction(TypeField.instance, Query::Restriction::AnyOf, types)
|
129
|
+
end
|
130
|
+
dsl = DSL::Scope.new(conjunction, setup_for_types(types))
|
131
|
+
Util.instance_eval_or_call(dsl, &block)
|
132
|
+
indexer.remove_by_scope(conjunction)
|
133
|
+
else
|
134
|
+
objects.flatten!
|
135
|
+
@deletes += objects.length
|
136
|
+
objects.each do |object|
|
137
|
+
indexer.remove(object)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# See Sunspot.remove!
|
144
|
+
#
|
145
|
+
def remove!(*objects)
|
146
|
+
remove(*objects)
|
147
|
+
commit
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# See Sunspot.remove_by_id
|
152
|
+
#
|
153
|
+
def remove_by_id(clazz, id)
|
154
|
+
class_name =
|
155
|
+
if clazz.is_a?(Class)
|
156
|
+
clazz.name
|
157
|
+
else
|
158
|
+
clazz.to_s
|
159
|
+
end
|
160
|
+
indexer.remove_by_id(class_name, id)
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# See Sunspot.remove_by_id!
|
165
|
+
#
|
166
|
+
def remove_by_id!(clazz, id)
|
167
|
+
remove_by_id(clazz, id)
|
168
|
+
commit
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# See Sunspot.remove_all
|
173
|
+
#
|
174
|
+
def remove_all(*classes)
|
175
|
+
classes.flatten!
|
176
|
+
if classes.empty?
|
177
|
+
@deletes += 1
|
178
|
+
indexer.remove_all
|
179
|
+
else
|
180
|
+
@deletes += classes.length
|
181
|
+
classes.each { |clazz| indexer.remove_all(clazz) }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# See Sunspot.remove_all!
|
187
|
+
#
|
188
|
+
def remove_all!(*classes)
|
189
|
+
remove_all(*classes)
|
190
|
+
commit
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# See Sunspot.dirty?
|
195
|
+
#
|
196
|
+
def dirty?
|
197
|
+
(@deletes + @adds) > 0
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
# See Sunspot.commit_if_dirty
|
202
|
+
#
|
203
|
+
def commit_if_dirty
|
204
|
+
commit if dirty?
|
205
|
+
end
|
206
|
+
|
207
|
+
#
|
208
|
+
# See Sunspot.delete_dirty?
|
209
|
+
#
|
210
|
+
def delete_dirty?
|
211
|
+
@deletes > 0
|
212
|
+
end
|
213
|
+
|
214
|
+
#
|
215
|
+
# See Sunspot.commit_if_delete_dirty
|
216
|
+
#
|
217
|
+
def commit_if_delete_dirty
|
218
|
+
commit if delete_dirty?
|
219
|
+
end
|
220
|
+
|
221
|
+
#
|
222
|
+
# See Sunspot.batch
|
223
|
+
#
|
224
|
+
def batch
|
225
|
+
indexer.start_batch
|
226
|
+
yield
|
227
|
+
indexer.flush_batch
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
#
|
233
|
+
# Retrieve the Solr connection for this session, creating one if it does not
|
234
|
+
# already exist.
|
235
|
+
#
|
236
|
+
# ==== Returns
|
237
|
+
#
|
238
|
+
# RSolr::Connection::Base:: The connection for this session
|
239
|
+
#
|
240
|
+
def connection
|
241
|
+
@connection ||=
|
242
|
+
self.class.connection_class.connect(:url => config.solr.url,
|
243
|
+
:read_timeout => config.solr.read_timeout,
|
244
|
+
:open_timeout => config.solr.open_timeout)
|
245
|
+
end
|
246
|
+
|
247
|
+
def indexer
|
248
|
+
@indexer ||= Indexer.new(connection)
|
249
|
+
end
|
250
|
+
|
251
|
+
def setup_for_types(types)
|
252
|
+
if types.empty?
|
253
|
+
raise(ArgumentError, "You must specify at least one type to search")
|
254
|
+
end
|
255
|
+
if types.length == 1
|
256
|
+
Setup.for(types.first)
|
257
|
+
else
|
258
|
+
CompositeSetup.for(types)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module SessionProxy
|
3
|
+
class AbstractSessionProxy #:nodoc:
|
4
|
+
class <<self
|
5
|
+
def delegate(*args)
|
6
|
+
options = Util.extract_options_from(args)
|
7
|
+
delegate = options[:to]
|
8
|
+
args.each do |method|
|
9
|
+
module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
10
|
+
def #{method}(*args, &block)
|
11
|
+
#{delegate}.#{method}(*args, &block)
|
12
|
+
end
|
13
|
+
RUBY
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def not_supported(*methods)
|
18
|
+
methods.each do |method|
|
19
|
+
module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
20
|
+
def #{method}(*args, &block)
|
21
|
+
raise NotSupportedError, "#{name} does not support #{method.inspect}"
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|