outoftime-sunspot 0.0.2 → 0.7.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/History.txt +4 -0
- data/README.rdoc +26 -33
- data/TODO +7 -0
- data/VERSION.yml +2 -2
- data/lib/light_config.rb +5 -5
- data/lib/sunspot/adapters.rb +209 -33
- data/lib/sunspot/configuration.rb +25 -10
- data/lib/sunspot/dsl/fields.rb +42 -11
- data/lib/sunspot/dsl/query.rb +150 -6
- data/lib/sunspot/dsl/scope.rb +16 -26
- data/lib/sunspot/facet.rb +37 -0
- data/lib/sunspot/facet_row.rb +34 -0
- data/lib/sunspot/facets.rb +21 -0
- data/lib/sunspot/field.rb +112 -56
- data/lib/sunspot/indexer.rb +49 -46
- data/lib/sunspot/query.rb +217 -49
- data/lib/sunspot/restriction.rb +158 -14
- data/lib/sunspot/search.rb +79 -28
- data/lib/sunspot/session.rb +75 -8
- data/lib/sunspot/setup.rb +171 -0
- data/lib/sunspot/type.rb +116 -32
- data/lib/sunspot/util.rb +141 -7
- data/lib/sunspot.rb +260 -31
- data/spec/api/build_search_spec.rb +139 -33
- data/spec/api/indexer_spec.rb +33 -2
- data/spec/api/search_retrieval_spec.rb +85 -2
- data/spec/api/session_spec.rb +14 -6
- data/spec/integration/faceting_spec.rb +39 -0
- data/spec/integration/keyword_search_spec.rb +1 -1
- data/spec/integration/scoped_search_spec.rb +175 -0
- data/spec/mocks/mock_adapter.rb +7 -10
- data/spec/mocks/post.rb +7 -2
- data/tasks/rdoc.rake +7 -0
- data/tasks/spec.rake +3 -0
- data/tasks/todo.rake +4 -0
- metadata +12 -7
- data/lib/sunspot/builder.rb +0 -78
- data/spec/api/standard_search_builder_spec.rb +0 -76
- data/spec/custom_expectation.rb +0 -53
- data/spec/integration/field_types_spec.rb +0 -62
data/lib/sunspot/dsl/query.rb
CHANGED
@@ -1,22 +1,146 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module DSL
|
3
|
+
#
|
4
|
+
# This class presents a DSL for constructing queries using the
|
5
|
+
# Sunspot.search method. Methods of this class are available inside the
|
6
|
+
# search block.
|
7
|
+
#
|
8
|
+
# See Sunspot.search for usage examples
|
9
|
+
#
|
3
10
|
class Query
|
4
|
-
|
5
|
-
|
11
|
+
NONE = Object.new
|
12
|
+
|
13
|
+
def initialize(query) #:nodoc:
|
14
|
+
@query = query
|
6
15
|
end
|
7
16
|
|
17
|
+
# Specify a phrase that should be searched as fulltext. Only +text+
|
18
|
+
# fields are searched - see DSL::Fields.text
|
19
|
+
#
|
20
|
+
# Note that the keywords are passed directly to Solr unadulterated. The
|
21
|
+
# advantage of this is that users can potentially use boolean logic to
|
22
|
+
# make advanced searches. The disadvantage is that syntax errors are
|
23
|
+
# possible. This may get better in a future version; suggestions are
|
24
|
+
# welcome.
|
25
|
+
#
|
26
|
+
# ==== Parameters
|
27
|
+
#
|
28
|
+
# keywords<String>:: phrase to perform fulltext search on
|
29
|
+
#
|
8
30
|
def keywords(keywords)
|
9
31
|
@query.keywords = keywords
|
10
32
|
end
|
11
33
|
|
12
|
-
|
13
|
-
|
34
|
+
#
|
35
|
+
# Build a positive restriction. With one argument, this method returns
|
36
|
+
# another DSL object which presents methods for attaching various
|
37
|
+
# restriction types. With two arguments, acts as a shorthand for creating
|
38
|
+
# an equality restriction.
|
39
|
+
#
|
40
|
+
# ==== Parameters
|
41
|
+
#
|
42
|
+
# field_name<Symbol>:: Name of the field on which to place the restriction
|
43
|
+
# value<Symbol>::
|
44
|
+
# If passed, creates an equality restriction with this value
|
45
|
+
#
|
46
|
+
# ==== Returns
|
47
|
+
#
|
48
|
+
# Sunspot::DSL::Restriction::
|
49
|
+
# Restriction DSL object (if only one argument is passed)
|
50
|
+
#
|
51
|
+
# ==== Examples
|
52
|
+
#
|
53
|
+
# An equality restriction:
|
54
|
+
#
|
55
|
+
# Sunspot.search do
|
56
|
+
# with(:blog_id, 1)
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# Other restriction types:
|
60
|
+
#
|
61
|
+
# Sunspot.search(Post) do
|
62
|
+
# with(:average_rating).greater_than(3.0)
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
def with(field_name, value = NONE)
|
66
|
+
if value == NONE
|
67
|
+
DSL::Restriction.new(field_name.to_sym, @query, false)
|
68
|
+
else
|
69
|
+
@query.add_restriction(field_name, Sunspot::Restriction::EqualTo, value, false)
|
70
|
+
end
|
14
71
|
end
|
15
72
|
|
16
|
-
|
17
|
-
|
73
|
+
#
|
74
|
+
# Build a negative restriction (exclusion). This method can take three
|
75
|
+
# forms: equality exclusion, exclusion by another restriction, or identity
|
76
|
+
# exclusion. The first two forms work the same way as the #with method;
|
77
|
+
# the third excludes a specific instance from the search results.
|
78
|
+
#
|
79
|
+
# ==== Parameters (exclusion by field value)
|
80
|
+
#
|
81
|
+
# field_name<Symbol>:: Name of the field on which to place the exclusion
|
82
|
+
# value<Symbol>::
|
83
|
+
# If passed, creates an equality exclusion with this value
|
84
|
+
#
|
85
|
+
# ==== Parameters (exclusion by identity)
|
86
|
+
#
|
87
|
+
# args<Object>...::
|
88
|
+
# One or more instances that should be excluded from the results
|
89
|
+
#
|
90
|
+
# ==== Examples
|
91
|
+
#
|
92
|
+
# An equality exclusion:
|
93
|
+
#
|
94
|
+
# Sunspot.search(Post) do
|
95
|
+
# without(:blog_id, 1)
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# Other restriction types:
|
99
|
+
#
|
100
|
+
# Sunspot.search(Post) do
|
101
|
+
# without(:average_rating).greater_than(3.0)
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# Exclusion by identity:
|
105
|
+
#
|
106
|
+
# Sunspot.search(Post) do
|
107
|
+
# without(some_post_instance)
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
def without(*args)
|
111
|
+
case args.first
|
112
|
+
when String, Symbol
|
113
|
+
field_name = args[0]
|
114
|
+
value = args.length > 1 ? args[1] : NONE
|
115
|
+
if value == NONE
|
116
|
+
DSL::Restriction.new(field_name.to_sym, @query, true)
|
117
|
+
else
|
118
|
+
@query.add_restriction(field_name, Sunspot::Restriction::EqualTo, value, true)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
instances = args
|
122
|
+
for instance in instances.flatten
|
123
|
+
@query.add_component(Sunspot::Restriction::SameAs.new(instance, true))
|
124
|
+
end
|
125
|
+
end
|
18
126
|
end
|
19
127
|
|
128
|
+
# Paginate your search. This works the same way as WillPaginate's
|
129
|
+
# paginate().
|
130
|
+
#
|
131
|
+
# Note that Solr searches are _always_ paginated. Not calling #paginate is
|
132
|
+
# the equivalent of calling:
|
133
|
+
#
|
134
|
+
# paginate(:page => 1, :per_page => Sunspot.config.pagination.default_per_page)
|
135
|
+
#
|
136
|
+
# ==== Options (options)
|
137
|
+
#
|
138
|
+
# :page<Integer>:: The requested page (required)
|
139
|
+
#
|
140
|
+
# :per_page<Integer>::
|
141
|
+
# How many results to return per page. The default is the value in
|
142
|
+
# +Sunspot.config.pagination.default_per_page+
|
143
|
+
#
|
20
144
|
def paginate(options = {})
|
21
145
|
page = options.delete(:page) || raise(ArgumentError, "paginate requires a :page argument")
|
22
146
|
per_page = options.delete(:per_page)
|
@@ -24,9 +148,29 @@ module Sunspot
|
|
24
148
|
@query.paginate(page, per_page)
|
25
149
|
end
|
26
150
|
|
151
|
+
# Specify the order that results should be returned in. This method can
|
152
|
+
# be called multiple times; precedence will be in the order given.
|
153
|
+
#
|
154
|
+
# ==== Parameters
|
155
|
+
#
|
156
|
+
# field_name<Symbol>:: the field to use for ordering
|
157
|
+
# direction<Symbol>:: :asc or :desc (default :asc)
|
158
|
+
#
|
27
159
|
def order_by(field_name, direction = nil)
|
28
160
|
@query.order_by(field_name, direction)
|
29
161
|
end
|
162
|
+
|
163
|
+
# Request facets on the given field names. See Sunspot::Search#facet and
|
164
|
+
# Sunspot::Facet for information on what is returned.
|
165
|
+
#
|
166
|
+
# ==== Parameters
|
167
|
+
#
|
168
|
+
# field_names...<Symbol>:: fields for which to return field facets
|
169
|
+
def facet(*field_names)
|
170
|
+
for field_name in field_names
|
171
|
+
@query.add_field_facet(field_name)
|
172
|
+
end
|
173
|
+
end
|
30
174
|
end
|
31
175
|
end
|
32
176
|
end
|
data/lib/sunspot/dsl/scope.rb
CHANGED
@@ -1,34 +1,24 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module DSL
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
#
|
4
|
+
# This class presents an API for building restrictions in the query DSL. The
|
5
|
+
# methods exposed are the snake-cased names of the classes defined in the
|
6
|
+
# Restriction module, with the exception of Base and SameAs. All methods
|
7
|
+
# take a single argument, which is the value to be applied to the
|
8
|
+
# restriction.
|
9
|
+
#
|
10
|
+
class Restriction
|
11
|
+
def initialize(field_name, query, negative)
|
12
|
+
@field_name, @query, @negative = field_name, query, negative
|
6
13
|
end
|
7
14
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
class RestrictionBuilder
|
16
|
-
def initialize(field_name, query)
|
17
|
-
@field_name, @query = field_name, query
|
18
|
-
end
|
19
|
-
|
20
|
-
def method_missing(condition_name, *args)
|
21
|
-
clazz = begin
|
22
|
-
::Sunspot::Restriction.const_get(condition_name.to_s.camel_case)
|
23
|
-
rescue(NameError)
|
24
|
-
super(condition_name.to_sym, *args)
|
25
|
-
end
|
26
|
-
if value = args.first
|
27
|
-
@query.add_scope @query.build_condition(@field_name, clazz, args.first)
|
28
|
-
else
|
29
|
-
@query.interpret_condition @field_name, clazz
|
15
|
+
Sunspot::Restriction.names.each do |class_name|
|
16
|
+
method_name = Util.snake_case(class_name)
|
17
|
+
module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
18
|
+
def #{method_name}(value)
|
19
|
+
@query.add_restriction(@field_name, Sunspot::Restriction::#{class_name}, value, @negative)
|
30
20
|
end
|
31
|
-
|
21
|
+
RUBY
|
32
22
|
end
|
33
23
|
end
|
34
24
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Sunspot
|
2
|
+
#
|
3
|
+
# The facet class encapsulates the information returned by Solr for a
|
4
|
+
# particular facet request.
|
5
|
+
#
|
6
|
+
# See http://wiki.apache.org/solr/SolrFacetingOverview for more information
|
7
|
+
# on Solr's faceting capabilities.
|
8
|
+
#
|
9
|
+
class Facet
|
10
|
+
def initialize(facet_values, field) #:nodoc:
|
11
|
+
@facet_values, @field = facet_values, field
|
12
|
+
end
|
13
|
+
|
14
|
+
# The name of the field that contains this facet's values
|
15
|
+
#
|
16
|
+
# ==== Returns
|
17
|
+
#
|
18
|
+
# Symbol:: The field name
|
19
|
+
#
|
20
|
+
def field_name
|
21
|
+
@field.name
|
22
|
+
end
|
23
|
+
|
24
|
+
# The rows returned for this facet.
|
25
|
+
#
|
26
|
+
# ==== Returns
|
27
|
+
#
|
28
|
+
# Array:: Collection of FacetRow objects, in the order returned by Solr
|
29
|
+
#
|
30
|
+
def rows
|
31
|
+
@rows ||=
|
32
|
+
@facet_values.map do |facet_value|
|
33
|
+
FacetRow.new(facet_value, @field)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Sunspot
|
2
|
+
# This class encapsulates a facet row (value) for a facet.
|
3
|
+
class FacetRow
|
4
|
+
def initialize(facet_value, field) #:nodoc:
|
5
|
+
@facet_value, @field = facet_value, field
|
6
|
+
end
|
7
|
+
|
8
|
+
# The value associated with the facet. This will be cast according to the
|
9
|
+
# field's type; so, for an integer field, this method will return an
|
10
|
+
# integer, etc.
|
11
|
+
#
|
12
|
+
# Note that <strong>+Time+ fields will always return facet values in
|
13
|
+
# UTC</strong>.
|
14
|
+
#
|
15
|
+
# ==== Returns
|
16
|
+
#
|
17
|
+
# Object:: The value associated with the row, cast to the appropriate type
|
18
|
+
#
|
19
|
+
def value
|
20
|
+
@value ||= @field.cast(@facet_value.name)
|
21
|
+
end
|
22
|
+
|
23
|
+
# The number of documents matching the search parameters that have this
|
24
|
+
# value in the facet's field.
|
25
|
+
#
|
26
|
+
# ==== Returns
|
27
|
+
#
|
28
|
+
# Integer:: Document count for this value
|
29
|
+
#
|
30
|
+
def count
|
31
|
+
@count ||= @facet_value.value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module Facets
|
3
|
+
#
|
4
|
+
# Encapsulates a query component representing a field facet. Users create
|
5
|
+
# instances using DSL::Query#facet
|
6
|
+
#
|
7
|
+
class FieldFacet #:nodoc:
|
8
|
+
def initialize(field)
|
9
|
+
@field = field
|
10
|
+
end
|
11
|
+
|
12
|
+
# ==== Returns
|
13
|
+
#
|
14
|
+
# Hash:: solr-ruby params for this field facet
|
15
|
+
#
|
16
|
+
def to_params
|
17
|
+
{ :facets => { :fields => [@field.indexed_name] }}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/sunspot/field.rb
CHANGED
@@ -1,39 +1,72 @@
|
|
1
1
|
module Sunspot
|
2
|
-
module Field
|
2
|
+
module Field #:nodoc[all]
|
3
|
+
#
|
4
|
+
# Field classes encapsulate information about a field that has been configured
|
5
|
+
# for search and indexing. They expose methods that are useful for both
|
6
|
+
# operations.
|
7
|
+
#
|
8
|
+
# Subclasses of Field::Base must implement the method #value_for
|
9
|
+
#
|
3
10
|
class Base
|
4
|
-
attr_accessor :name
|
11
|
+
attr_accessor :name # The public-facing name of the field
|
12
|
+
attr_accessor :type # The Type of the field
|
5
13
|
|
6
|
-
def initialize(name, type, options = {})
|
7
|
-
@name, @type = name, type
|
14
|
+
def initialize(name, type, options = {}) #:nodoc
|
15
|
+
@name, @type = name.to_sym, type
|
8
16
|
@multiple = options.delete(:multiple)
|
9
17
|
raise ArgumentError, "Unknown field option #{options.keys.first.inspect} provided for field #{name.inspect}" unless options.empty?
|
10
18
|
end
|
11
19
|
|
20
|
+
# A key-value pair where the key is the field's indexed name and the
|
21
|
+
# value is the value that should be indexed for the given model. This can
|
22
|
+
# be merged directly into the document hash for adding to solr-ruby.
|
23
|
+
#
|
24
|
+
# ==== Parameters
|
25
|
+
#
|
26
|
+
# model<Object>:: the model from which to extract the value
|
27
|
+
#
|
28
|
+
# ==== Returns
|
29
|
+
#
|
30
|
+
# Hash:: a single key-value pair with the field name and value
|
31
|
+
#
|
12
32
|
def pair_for(model)
|
13
|
-
|
33
|
+
unless (value = value_for(model)).nil?
|
14
34
|
{ indexed_name.to_sym => to_indexed(value) }
|
15
35
|
else
|
16
36
|
{}
|
17
37
|
end
|
18
38
|
end
|
19
39
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
name.hash + 31 * type.hash
|
29
|
-
end
|
30
|
-
|
40
|
+
# The name of the field as it is indexed in Solr. The indexed name
|
41
|
+
# contains a suffix that contains information about the type as well as
|
42
|
+
# whether the field allows multiple values for a document.
|
43
|
+
#
|
44
|
+
# ==== Returns
|
45
|
+
#
|
46
|
+
# String:: The field's indexed name
|
47
|
+
#
|
31
48
|
def indexed_name
|
32
49
|
"#{type.indexed_name(name)}#{'m' if multiple?}"
|
33
50
|
end
|
34
51
|
|
52
|
+
# Convert a value to its representation for Solr indexing. This delegates
|
53
|
+
# to the #to_indexed method on the field's type.
|
54
|
+
#
|
55
|
+
# ==== Parameters
|
56
|
+
#
|
57
|
+
# value<Object>:: Value to convert to Solr representation
|
58
|
+
#
|
59
|
+
# ==== Returns
|
60
|
+
#
|
61
|
+
# String:: Solr representation of the object
|
62
|
+
#
|
63
|
+
# ==== Raises
|
64
|
+
#
|
65
|
+
# ArgumentError::
|
66
|
+
# the value is an array, but this field does not allow multiple values
|
67
|
+
#
|
35
68
|
def to_indexed(value)
|
36
|
-
if value.
|
69
|
+
if value.is_a? Array
|
37
70
|
if multiple?
|
38
71
|
value.map { |val| to_indexed(val) }
|
39
72
|
else
|
@@ -44,65 +77,88 @@ module Sunspot
|
|
44
77
|
end
|
45
78
|
end
|
46
79
|
|
80
|
+
# Cast the value into the appropriate Ruby class for the field's type
|
81
|
+
#
|
82
|
+
# ==== Parameters
|
83
|
+
#
|
84
|
+
# value<String>:: Solr's representation of the value
|
85
|
+
#
|
86
|
+
# ==== Returns
|
87
|
+
#
|
88
|
+
# Object:: The cast value
|
89
|
+
#
|
90
|
+
def cast(value)
|
91
|
+
type.cast(value)
|
92
|
+
end
|
93
|
+
|
94
|
+
# ==== Returns
|
95
|
+
#
|
96
|
+
# Boolean:: true if the field allows multiple values; false if not
|
47
97
|
def multiple?
|
48
98
|
!!@multiple
|
49
99
|
end
|
50
100
|
end
|
51
101
|
|
52
|
-
|
102
|
+
#
|
103
|
+
# AttributeFields call methods directly on indexed objects and index the
|
104
|
+
# return value of the method. By default, the field name is also the
|
105
|
+
# attribute that provides the value for indexing, but this can be overridden
|
106
|
+
# with the :using option.
|
107
|
+
#
|
108
|
+
class AttributeField < Base
|
109
|
+
def initialize(name, type, options = {})
|
110
|
+
@attribute_name = options.delete(:using) || name
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
53
114
|
protected
|
54
115
|
|
116
|
+
#
|
117
|
+
# Call the field's attribute name on the given model and return the value.
|
118
|
+
#
|
119
|
+
# ==== Parameters
|
120
|
+
#
|
121
|
+
# model<Object>:: The object from which to extract the value
|
122
|
+
#
|
123
|
+
# ==== Returns
|
124
|
+
#
|
125
|
+
# Object:: The value to index
|
126
|
+
#
|
55
127
|
def value_for(model)
|
56
|
-
model.send(
|
128
|
+
model.send(@attribute_name)
|
57
129
|
end
|
58
130
|
end
|
59
131
|
|
60
|
-
|
132
|
+
#
|
133
|
+
# VirtualFields extract data by evaluating the provided block in the context
|
134
|
+
# of the model instance.
|
135
|
+
#
|
136
|
+
class VirtualField < Base
|
61
137
|
def initialize(name, type, options = {}, &block)
|
62
138
|
super(name, type, options)
|
63
139
|
@block = block
|
64
140
|
end
|
65
141
|
|
66
142
|
protected
|
67
|
-
attr_accessor :block
|
68
143
|
|
144
|
+
#
|
145
|
+
# Evaluate the block in the model's context and return the block's return
|
146
|
+
# value.
|
147
|
+
#
|
148
|
+
# ==== Parameters
|
149
|
+
#
|
150
|
+
# model<Object>:: The object from which to extract the value
|
151
|
+
#
|
152
|
+
# ==== Returns
|
153
|
+
#
|
154
|
+
# Object:: The value to index
|
69
155
|
def value_for(model)
|
70
|
-
|
156
|
+
if @block.arity <= 0
|
157
|
+
model.instance_eval(&@block)
|
158
|
+
else
|
159
|
+
@block.call(model)
|
160
|
+
end
|
71
161
|
end
|
72
162
|
end
|
73
163
|
end
|
74
|
-
|
75
|
-
class <<Field
|
76
|
-
def register(clazz, fields)
|
77
|
-
fields = [fields] unless fields.kind_of? Enumerable
|
78
|
-
self.for(clazz).concat fields
|
79
|
-
end
|
80
|
-
|
81
|
-
def register_text(clazz, fields)
|
82
|
-
fields = [fields] unless fields.kind_of? Enumerable
|
83
|
-
self.text_for(clazz).concat fields
|
84
|
-
end
|
85
|
-
|
86
|
-
def text_for(clazz)
|
87
|
-
keyword_fields_hash[clazz.object_id] ||= []
|
88
|
-
end
|
89
|
-
|
90
|
-
def for(clazz)
|
91
|
-
fields_hash[clazz.object_id] ||= []
|
92
|
-
end
|
93
|
-
|
94
|
-
def unregister_all!
|
95
|
-
fields_hash.clear
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def fields_hash
|
101
|
-
@fields_hash ||= {}
|
102
|
-
end
|
103
|
-
|
104
|
-
def keyword_fields_hash
|
105
|
-
@keyword_fields_hash ||= {}
|
106
|
-
end
|
107
|
-
end
|
108
164
|
end
|
data/lib/sunspot/indexer.rb
CHANGED
@@ -1,65 +1,68 @@
|
|
1
1
|
module Sunspot
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
#
|
3
|
+
# This class presents a service for adding, updating, and removing data
|
4
|
+
# from the Solr index. An Indexer instance is associated with a particular
|
5
|
+
# setup, and thus is capable of indexing instances of a certain class (and its
|
6
|
+
# subclasses).
|
7
|
+
#
|
8
|
+
class Indexer #:nodoc:
|
9
|
+
def initialize(connection, setup)
|
10
|
+
@connection, @setup = connection, setup
|
5
11
|
end
|
6
12
|
|
13
|
+
#
|
14
|
+
# Construct a representation of the model for indexing and send it to the
|
15
|
+
# connection for indexing
|
16
|
+
#
|
17
|
+
# ==== Parameters
|
18
|
+
#
|
19
|
+
# model<Object>:: the model to index
|
20
|
+
#
|
7
21
|
def add(model)
|
8
|
-
hash = static_hash_for
|
9
|
-
for field in
|
10
|
-
hash.merge!
|
22
|
+
hash = static_hash_for(model)
|
23
|
+
for field in @setup.all_fields
|
24
|
+
hash.merge!(field.pair_for(model))
|
11
25
|
end
|
12
|
-
connection.add
|
13
|
-
end
|
14
|
-
|
15
|
-
def fields
|
16
|
-
@fields ||= []
|
17
|
-
end
|
18
|
-
|
19
|
-
def add_fields(fields)
|
20
|
-
self.fields.concat fields
|
26
|
+
@connection.add(hash)
|
21
27
|
end
|
22
28
|
|
29
|
+
#
|
30
|
+
# Remove the given model from the Solr index
|
31
|
+
#
|
23
32
|
def remove(model)
|
24
|
-
connection.delete(::
|
33
|
+
@connection.delete(Adapters::InstanceAdapter.adapt(model).index_id)
|
25
34
|
end
|
26
35
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def
|
31
|
-
{
|
32
|
-
:type => Indexer.superclasses_for(model.class).map { |clazz| clazz.name }}
|
36
|
+
#
|
37
|
+
# Delete all documents of the class indexed by this indexer from Solr.
|
38
|
+
#
|
39
|
+
def remove_all
|
40
|
+
@connection.delete_by_query("type:#{@setup.clazz.name}")
|
33
41
|
end
|
34
|
-
end
|
35
42
|
|
36
|
-
|
37
|
-
def add(connection, model)
|
38
|
-
self.for(model.class, connection).add(model)
|
39
|
-
end
|
40
|
-
|
41
|
-
def remove(connection, model)
|
42
|
-
self.for(model.class, connection).remove(model)
|
43
|
-
end
|
43
|
+
protected
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
#
|
46
|
+
# All indexed documents index and store the +id+ and +type+ fields.
|
47
|
+
# This method constructs the document hash containing those key-value
|
48
|
+
# pairs.
|
49
|
+
#
|
50
|
+
def static_hash_for(model)
|
51
|
+
{ :id => Adapters::InstanceAdapter.adapt(model).index_id,
|
52
|
+
:type => Util.superclasses_for(model.class).map { |clazz| clazz.name }}
|
47
53
|
end
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
55
|
+
class <<self
|
56
|
+
#
|
57
|
+
# Delete all documents from the Solr index
|
58
|
+
#
|
59
|
+
# ==== Parameters
|
60
|
+
#
|
61
|
+
# connection<Solr::Connection>::
|
62
|
+
# connection to which to send the delete request
|
63
|
+
def remove_all(connection)
|
64
|
+
connection.delete_by_query("type:[* TO *]")
|
54
65
|
end
|
55
|
-
raise ArgumentError, "Class #{clazz.name} has not been configured for indexing" if indexer.fields.empty?
|
56
|
-
indexer
|
57
|
-
end
|
58
|
-
|
59
|
-
def superclasses_for(clazz)
|
60
|
-
superclasses_for = [clazz]
|
61
|
-
superclasses_for << (clazz = clazz.superclass) while clazz.superclass != Object
|
62
|
-
superclasses_for
|
63
66
|
end
|
64
67
|
end
|
65
68
|
end
|