outoftime-sunspot 0.0.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|