UnderpantsGnome-sunspot 0.9.1.1
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 +39 -0
- data/LICENSE +18 -0
- data/README.rdoc +154 -0
- data/Rakefile +9 -0
- data/TODO +4 -0
- data/VERSION.yml +4 -0
- data/bin/sunspot-configure-solr +46 -0
- data/bin/sunspot-solr +62 -0
- data/lib/light_config.rb +40 -0
- data/lib/sunspot.rb +470 -0
- data/lib/sunspot/adapters.rb +265 -0
- data/lib/sunspot/composite_setup.rb +186 -0
- data/lib/sunspot/configuration.rb +38 -0
- data/lib/sunspot/data_extractor.rb +47 -0
- data/lib/sunspot/date_facet.rb +36 -0
- data/lib/sunspot/date_facet_row.rb +17 -0
- data/lib/sunspot/dsl.rb +3 -0
- data/lib/sunspot/dsl/field_query.rb +72 -0
- data/lib/sunspot/dsl/fields.rb +86 -0
- data/lib/sunspot/dsl/query.rb +59 -0
- data/lib/sunspot/dsl/query_facet.rb +31 -0
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/scope.rb +193 -0
- data/lib/sunspot/dsl/search.rb +30 -0
- data/lib/sunspot/facet.rb +51 -0
- data/lib/sunspot/facet_row.rb +34 -0
- data/lib/sunspot/field.rb +157 -0
- data/lib/sunspot/field_factory.rb +126 -0
- data/lib/sunspot/indexer.rb +127 -0
- data/lib/sunspot/instantiated_facet.rb +38 -0
- data/lib/sunspot/instantiated_facet_row.rb +12 -0
- data/lib/sunspot/query.rb +190 -0
- data/lib/sunspot/query/base_query.rb +90 -0
- data/lib/sunspot/query/connective.rb +77 -0
- data/lib/sunspot/query/dynamic_query.rb +69 -0
- data/lib/sunspot/query/field_facet.rb +149 -0
- data/lib/sunspot/query/field_query.rb +57 -0
- data/lib/sunspot/query/pagination.rb +39 -0
- data/lib/sunspot/query/query_facet.rb +72 -0
- data/lib/sunspot/query/query_facet_row.rb +19 -0
- data/lib/sunspot/query/restriction.rb +225 -0
- data/lib/sunspot/query/scope.rb +165 -0
- data/lib/sunspot/query/sort.rb +36 -0
- data/lib/sunspot/query/sort_composite.rb +33 -0
- data/lib/sunspot/query_facet.rb +33 -0
- data/lib/sunspot/query_facet_row.rb +21 -0
- data/lib/sunspot/schema.rb +165 -0
- data/lib/sunspot/search.rb +222 -0
- data/lib/sunspot/search/hit.rb +62 -0
- data/lib/sunspot/session.rb +201 -0
- data/lib/sunspot/setup.rb +271 -0
- data/lib/sunspot/type.rb +200 -0
- data/lib/sunspot/util.rb +164 -0
- data/solr/etc/jetty.xml +212 -0
- data/solr/etc/webdefault.xml +379 -0
- data/solr/lib/jetty-6.1.3.jar +0 -0
- data/solr/lib/jetty-util-6.1.3.jar +0 -0
- data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
- data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
- data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
- data/solr/solr/conf/elevate.xml +36 -0
- data/solr/solr/conf/protwords.txt +21 -0
- data/solr/solr/conf/schema.xml +50 -0
- data/solr/solr/conf/solrconfig.xml +696 -0
- data/solr/solr/conf/stopwords.txt +57 -0
- data/solr/solr/conf/synonyms.txt +31 -0
- data/solr/start.jar +0 -0
- data/solr/webapps/solr.war +0 -0
- data/spec/api/adapters_spec.rb +33 -0
- data/spec/api/build_search_spec.rb +918 -0
- data/spec/api/indexer_spec.rb +311 -0
- data/spec/api/query_spec.rb +153 -0
- data/spec/api/search_retrieval_spec.rb +325 -0
- data/spec/api/session_spec.rb +157 -0
- data/spec/api/spec_helper.rb +1 -0
- data/spec/api/sunspot_spec.rb +18 -0
- data/spec/integration/dynamic_fields_spec.rb +55 -0
- data/spec/integration/faceting_spec.rb +169 -0
- data/spec/integration/keyword_search_spec.rb +83 -0
- data/spec/integration/scoped_search_spec.rb +188 -0
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/integration/stored_fields_spec.rb +10 -0
- data/spec/integration/test_pagination.rb +32 -0
- data/spec/mocks/adapters.rb +32 -0
- data/spec/mocks/blog.rb +3 -0
- data/spec/mocks/comment.rb +19 -0
- data/spec/mocks/connection.rb +84 -0
- data/spec/mocks/mock_adapter.rb +30 -0
- data/spec/mocks/mock_record.rb +41 -0
- data/spec/mocks/photo.rb +8 -0
- data/spec/mocks/post.rb +70 -0
- data/spec/mocks/user.rb +8 -0
- data/spec/spec_helper.rb +47 -0
- data/tasks/gemspec.rake +25 -0
- data/tasks/rcov.rake +28 -0
- data/tasks/rdoc.rake +21 -0
- data/tasks/schema.rake +19 -0
- data/tasks/spec.rake +24 -0
- data/tasks/todo.rake +4 -0
- data/templates/schema.xml.haml +24 -0
- metadata +245 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module DSL
|
3
|
+
#
|
4
|
+
# This top-level DSL class is the context in which the block passed to
|
5
|
+
# Sunspot.query. See Sunspot::DSL::Query, Sunspot::DSL::FieldQuery, and
|
6
|
+
# Sunspot::DSL::Scope for the full API presented.
|
7
|
+
#
|
8
|
+
class Search < Query
|
9
|
+
def initialize(search) #:nodoc:
|
10
|
+
@search = search
|
11
|
+
@query = search.query
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Retrieve the data accessor used to load instances of the given class
|
16
|
+
# out of persistent storage. Data accessors are free to implement any
|
17
|
+
# extra methods that may be useful in this context.
|
18
|
+
#
|
19
|
+
# ==== Example
|
20
|
+
#
|
21
|
+
# Sunspot.search Post do
|
22
|
+
# data_acccessor_for(Post).includes = [:blog, :comments]
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
def data_accessor_for(clazz)
|
26
|
+
@search.data_accessor_for(clazz)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
module Sunspot
|
4
|
+
#
|
5
|
+
# The facet class encapsulates the information returned by Solr for a
|
6
|
+
# field facet request.
|
7
|
+
#
|
8
|
+
# See http://wiki.apache.org/solr/SolrFacetingOverview for more information
|
9
|
+
# on Solr's faceting capabilities.
|
10
|
+
#
|
11
|
+
class Facet
|
12
|
+
attr_reader :field
|
13
|
+
|
14
|
+
def initialize(facet_values, field) #:nodoc:
|
15
|
+
@facet_values, @field = facet_values, field
|
16
|
+
end
|
17
|
+
|
18
|
+
# The name of the field that contains this facet's values
|
19
|
+
#
|
20
|
+
# ==== Returns
|
21
|
+
#
|
22
|
+
# Symbol:: The field name
|
23
|
+
#
|
24
|
+
def field_name
|
25
|
+
@field.name
|
26
|
+
end
|
27
|
+
|
28
|
+
# The rows returned for this facet.
|
29
|
+
#
|
30
|
+
# ==== Returns
|
31
|
+
#
|
32
|
+
# Array:: Collection of FacetRow objects, in the order returned by Solr
|
33
|
+
#
|
34
|
+
def rows
|
35
|
+
@rows ||=
|
36
|
+
begin
|
37
|
+
rows = []
|
38
|
+
@facet_values.each_slice(2) do |pair|
|
39
|
+
rows << new_row(pair)
|
40
|
+
end
|
41
|
+
rows
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def new_row(pair)
|
48
|
+
FacetRow.new(pair, self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
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(pair, facet) #:nodoc:
|
5
|
+
@pair, @facet = pair, facet
|
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 ||= @facet.field.cast(@pair[0])
|
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 ||= @pair[1]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module Sunspot
|
2
|
+
class Field #:nodoc:
|
3
|
+
attr_accessor :name # The public-facing name of the field
|
4
|
+
attr_accessor :type # The Type of the field
|
5
|
+
attr_accessor :reference # Model class that the value of this field refers to
|
6
|
+
attr_accessor :attributes
|
7
|
+
|
8
|
+
#
|
9
|
+
#
|
10
|
+
def initialize(name, type) #:nodoc
|
11
|
+
@name, @type = name.to_sym, type
|
12
|
+
@attributes = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Convert a value to its representation for Solr indexing. This delegates
|
16
|
+
# to the #to_indexed method on the field's type.
|
17
|
+
#
|
18
|
+
# ==== Parameters
|
19
|
+
#
|
20
|
+
# value<Object>:: Value to convert to Solr representation
|
21
|
+
#
|
22
|
+
# ==== Returns
|
23
|
+
#
|
24
|
+
# String:: Solr representation of the object
|
25
|
+
#
|
26
|
+
# ==== Raises
|
27
|
+
#
|
28
|
+
# ArgumentError::
|
29
|
+
# the value is an array, but this field does not allow multiple values
|
30
|
+
#
|
31
|
+
def to_indexed(value)
|
32
|
+
if value.is_a? Array
|
33
|
+
if @multiple
|
34
|
+
value.map { |val| to_indexed(val) }
|
35
|
+
else
|
36
|
+
raise ArgumentError, "#{name} is not a multiple-value field, so it cannot index values #{value.inspect}"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@type.to_indexed(value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Cast the value into the appropriate Ruby class for the field's type
|
44
|
+
#
|
45
|
+
# ==== Parameters
|
46
|
+
#
|
47
|
+
# value<String>:: Solr's representation of the value
|
48
|
+
#
|
49
|
+
# ==== Returns
|
50
|
+
#
|
51
|
+
# Object:: The cast value
|
52
|
+
#
|
53
|
+
def cast(value)
|
54
|
+
@type.cast(value)
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Name with which this field is indexed internally. Based on public name and
|
59
|
+
# type.
|
60
|
+
#
|
61
|
+
# ==== Returns
|
62
|
+
#
|
63
|
+
# String:: Internal name of the field
|
64
|
+
#
|
65
|
+
def indexed_name
|
66
|
+
@type.indexed_name(@name)
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Whether this field accepts multiple values.
|
71
|
+
#
|
72
|
+
# ==== Returns
|
73
|
+
#
|
74
|
+
# Boolean:: True if this field accepts multiple values.
|
75
|
+
#
|
76
|
+
def multiple?
|
77
|
+
!!@multiple
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# FulltextField instances represent fields that are indexed as fulltext.
|
83
|
+
# These fields are tokenized in the index, and can have boost applied to
|
84
|
+
# them. They also always allow multiple values (since the only downside of
|
85
|
+
# allowing multiple values is that it prevents the field from being sortable,
|
86
|
+
# and sorting on tokenized fields is nonsensical anyway, there is no reason
|
87
|
+
# to do otherwise). FulltextField instances always have the type TextType.
|
88
|
+
#
|
89
|
+
class FulltextField < Field #:nodoc:
|
90
|
+
def initialize(name, options = {})
|
91
|
+
super(name, Type::TextType)
|
92
|
+
if options.has_key?(:boost)
|
93
|
+
@attributes[:boost] = options.delete(:boost)
|
94
|
+
end
|
95
|
+
@multiple = true
|
96
|
+
raise ArgumentError, "Unknown field option #{options.keys.first.inspect} provided for field #{name.inspect}" unless options.empty?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# AttributeField instances encapsulate non-tokenized attribute data.
|
102
|
+
# AttributeFields can have any type except TextType, and can also have
|
103
|
+
# a reference (for instantiated facets), optionally allow multiple values
|
104
|
+
# (false by default), and can store their values (false by default). All
|
105
|
+
# scoping, sorting, and faceting is done with attribute fields.
|
106
|
+
#
|
107
|
+
class AttributeField < Field #:nodoc:
|
108
|
+
def initialize(name, type, options = {})
|
109
|
+
super(name, type)
|
110
|
+
@multiple = !!options.delete(:multiple)
|
111
|
+
@reference =
|
112
|
+
if (reference = options.delete(:references)).respond_to?(:name)
|
113
|
+
reference.name
|
114
|
+
elsif reference.respond_to?(:to_sym)
|
115
|
+
reference.to_sym
|
116
|
+
end
|
117
|
+
@stored = !!options.delete(:stored)
|
118
|
+
raise ArgumentError, "Unknown field option #{options.keys.first.inspect} provided for field #{name.inspect}" unless options.empty?
|
119
|
+
end
|
120
|
+
|
121
|
+
# The name of the field as it is indexed in Solr. The indexed name
|
122
|
+
# contains a suffix that contains information about the type as well as
|
123
|
+
# whether the field allows multiple values for a document.
|
124
|
+
#
|
125
|
+
# ==== Returns
|
126
|
+
#
|
127
|
+
# String:: The field's indexed name
|
128
|
+
#
|
129
|
+
def indexed_name
|
130
|
+
"#{super}#{'m' if @multiple}#{'s' if @stored}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# RandomField instances are used for random sorting.
|
136
|
+
#
|
137
|
+
class RandomField #:nodoc:
|
138
|
+
#
|
139
|
+
# Never multiple, but this has to return false so Sunspot doesn't barf
|
140
|
+
# when you try to order by it.
|
141
|
+
#
|
142
|
+
def multiple?
|
143
|
+
false
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Solr uses the dynamic field name as a seed for random, so we randomize the
|
148
|
+
# field name accordingly.
|
149
|
+
#
|
150
|
+
# #XXX I think it's bad to use a random number as a seed. Would it be
|
151
|
+
# better to pass in the current timestamp or some such thing?
|
152
|
+
#
|
153
|
+
def indexed_name
|
154
|
+
"random_#{rand(1<<16)}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Sunspot
|
2
|
+
#
|
3
|
+
# The FieldFactory module contains classes for generating fields. FieldFactory
|
4
|
+
# implementation classes should implement a #build method, although the arity
|
5
|
+
# of the method depends on the type of factory. They also must implement a
|
6
|
+
# #populate_document method, which extracts field data from a given model and
|
7
|
+
# adds it into the RSolr document for indexing.
|
8
|
+
#
|
9
|
+
module FieldFactory #:nodoc:all
|
10
|
+
#
|
11
|
+
# Base class for field factories.
|
12
|
+
#
|
13
|
+
class Abstract
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
def initialize(name, options = {}, &block)
|
17
|
+
@name = name.to_sym
|
18
|
+
@data_extractor =
|
19
|
+
if block
|
20
|
+
DataExtractor::BlockExtractor.new(&block)
|
21
|
+
else
|
22
|
+
DataExtractor::AttributeExtractor.new(options.delete(:using) || name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# A StaticFieldFactory generates normal static fields. Each factory instance
|
29
|
+
# contains an eager-initialized field instance, which is returned by the
|
30
|
+
# #build method.
|
31
|
+
#
|
32
|
+
class Static < Abstract
|
33
|
+
def initialize(name, type, options = {}, &block)
|
34
|
+
super(name, options, &block)
|
35
|
+
unless name.to_s =~ /^\w+$/
|
36
|
+
raise ArgumentError, "Invalid field name #{name}: only letters, numbers, and underscores are allowed."
|
37
|
+
end
|
38
|
+
@field =
|
39
|
+
if type == Type::TextType
|
40
|
+
FulltextField.new(name, options)
|
41
|
+
else
|
42
|
+
AttributeField.new(name, type, options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Return the field instance built by this factory
|
48
|
+
#
|
49
|
+
def build
|
50
|
+
@field
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Extract the encapsulated field's data from the given model and add it
|
55
|
+
# into the RSolr document for indexing.
|
56
|
+
#
|
57
|
+
def populate_document(document, model) #:nodoc:
|
58
|
+
unless (value = @data_extractor.value_for(model)).nil?
|
59
|
+
for scalar_value in Array(@field.to_indexed(value))
|
60
|
+
document.add_field(
|
61
|
+
@field.indexed_name.to_sym,
|
62
|
+
scalar_value, @field.attributes
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# A unique signature identifying this field by name and type.
|
70
|
+
#
|
71
|
+
def signature
|
72
|
+
[@field.name, @field.type]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# DynamicFieldFactories create dynamic field instances based on dynamic
|
78
|
+
# configuration.
|
79
|
+
#
|
80
|
+
class Dynamic < Abstract
|
81
|
+
attr_accessor :name, :type
|
82
|
+
|
83
|
+
def initialize(name, type, options = {}, &block)
|
84
|
+
super(name, options, &block)
|
85
|
+
@type, @options = type, options
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Build a field based on the dynamic name given.
|
90
|
+
#
|
91
|
+
def build(dynamic_name)
|
92
|
+
AttributeField.new("#{@name}:#{dynamic_name}", @type, @options.dup)
|
93
|
+
end
|
94
|
+
#
|
95
|
+
# This alias allows a DynamicFieldFactory to be used in place of a Setup
|
96
|
+
# or CompositeSetup instance by query components.
|
97
|
+
#
|
98
|
+
alias_method :field, :build
|
99
|
+
|
100
|
+
#
|
101
|
+
# Generate dynamic fields based on hash returned by data accessor and
|
102
|
+
# add the field data to the document.
|
103
|
+
#
|
104
|
+
def populate_document(document, model)
|
105
|
+
if values = @data_extractor.value_for(model)
|
106
|
+
values.each_pair do |dynamic_name, value|
|
107
|
+
field_instance = build(dynamic_name)
|
108
|
+
for scalar_value in Array(field_instance.to_indexed(value))
|
109
|
+
document.add_field(
|
110
|
+
field_instance.indexed_name.to_sym,
|
111
|
+
scalar_value
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Unique signature identifying this dynamic field based on name and type
|
120
|
+
#
|
121
|
+
def signature
|
122
|
+
[@name, @type]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Sunspot
|
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
|
+
include RSolr::Char
|
10
|
+
|
11
|
+
def initialize(connection)
|
12
|
+
@connection = connection
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Construct a representation of the model for indexing and send it to the
|
17
|
+
# connection for indexing
|
18
|
+
#
|
19
|
+
# ==== Parameters
|
20
|
+
#
|
21
|
+
# model<Object>:: the model to index
|
22
|
+
#
|
23
|
+
def add(model)
|
24
|
+
documents = Array(model).map { |m| prepare(m) }
|
25
|
+
if @batch.nil?
|
26
|
+
add_documents(documents)
|
27
|
+
else
|
28
|
+
@batch.concat(documents)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Remove the given model from the Solr index
|
34
|
+
#
|
35
|
+
def remove(model)
|
36
|
+
@connection.delete_by_id(Adapters::InstanceAdapter.adapt(model).index_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def remove_by_id(class_name, id)
|
40
|
+
@connection.delete_by_id(
|
41
|
+
Adapters::InstanceAdapter.index_id_for(class_name, id)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Delete all documents of the class indexed by this indexer from Solr.
|
47
|
+
#
|
48
|
+
def remove_all(clazz)
|
49
|
+
@connection.delete_by_query("type:#{escape(clazz.name)}")
|
50
|
+
end
|
51
|
+
|
52
|
+
def start_batch
|
53
|
+
@batch = []
|
54
|
+
end
|
55
|
+
|
56
|
+
def flush_batch
|
57
|
+
add_documents(@batch)
|
58
|
+
@batch = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
#
|
64
|
+
# Convert documents into hash of indexed properties
|
65
|
+
#
|
66
|
+
def prepare(model)
|
67
|
+
document = document_for(model)
|
68
|
+
setup = setup_for(model)
|
69
|
+
if boost = setup.document_boost_for(model)
|
70
|
+
document.attrs[:boost] = boost
|
71
|
+
end
|
72
|
+
for field_factory in setup.all_field_factories
|
73
|
+
field_factory.populate_document(document, model)
|
74
|
+
end
|
75
|
+
document
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_documents(documents)
|
79
|
+
begin
|
80
|
+
@connection.add(documents)
|
81
|
+
rescue Exception => e
|
82
|
+
puts e
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# All indexed documents index and store the +id+ and +type+ fields.
|
88
|
+
# This method constructs the document hash containing those key-value
|
89
|
+
# pairs.
|
90
|
+
#
|
91
|
+
def document_for(model)
|
92
|
+
RSolr::Message::Document.new(
|
93
|
+
:id => Adapters::InstanceAdapter.adapt(model).index_id,
|
94
|
+
:type => Util.superclasses_for(model.class).map { |clazz| clazz.name }
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Get the Setup object for the given object's class.
|
100
|
+
#
|
101
|
+
# ==== Parameters
|
102
|
+
#
|
103
|
+
# object<Object>:: The object whose setup is to be retrieved
|
104
|
+
#
|
105
|
+
# ==== Returns
|
106
|
+
#
|
107
|
+
# Sunspot::Setup:: The setup for the object's class
|
108
|
+
#
|
109
|
+
def setup_for(object)
|
110
|
+
Setup.for(object.class) || raise(NoSetupError, "Sunspot is not configured for #{object.class.inspect}")
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
class <<self
|
115
|
+
#
|
116
|
+
# Delete all documents from the Solr index
|
117
|
+
#
|
118
|
+
# ==== Parameters
|
119
|
+
#
|
120
|
+
# connection<Solr::Connection>::
|
121
|
+
# connection to which to send the delete request
|
122
|
+
def remove_all(connection)
|
123
|
+
connection.delete_by_query("type:[* TO *]")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|