-
Notifications
You must be signed in to change notification settings - Fork 922
/
field_factory.rb
178 lines (162 loc) · 5.28 KB
/
field_factory.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
module Sunspot
#
# The FieldFactory module contains classes for generating fields. FieldFactory
# implementation classes should implement a #build method, although the arity
# of the method depends on the type of factory. They also must implement a
# #populate_document method, which extracts field data from a given model and
# adds it into the Solr document for indexing.
#
module FieldFactory #:nodoc:all
#
# Base class for field factories.
#
class Abstract
attr_reader :name, :field
def initialize(name, options = {}, &block)
@name = name.to_sym
@data_extractor =
if block
DataExtractor::BlockExtractor.new(&block)
else
DataExtractor::AttributeExtractor.new(options.delete(:using) || name)
end
end
#
# Extract the encapsulated field's data from the given model and add it
# into the Solr document for indexing. (noop here for joins)
#
def populate_document(document, model, options = {}) #:nodoc:
end
protected
def extract_value(model, options = {})
if options.has_key?(:value)
options.delete(:value)
else
@data_extractor.value_for(model)
end
end
end
#
# A StaticFieldFactory generates normal static fields. Each factory instance
# contains an eager-initialized field instance, which is returned by the
# #build method.
#
class Static < Abstract
def initialize(name, type, options = {}, &block)
super(name, options, &block)
unless name.to_s =~ /^\w+$/
raise ArgumentError, "Invalid field name #{name}: only letters, numbers, and underscores are allowed."
end
@field =
if type.is_a?(Type::TextType)
FulltextField.new(name, options)
else
AttributeField.new(name, type, options)
end
end
#
# Return the field instance built by this factory
#
def build
@field
end
#
# Extract the encapsulated field's data from the given model and add it
# into the Solr document for indexing.
#
def populate_document(document, model, options = {}) #:nodoc:
atomic_operation = options[:update] == :set
value = extract_value(model, options)
if value != nil || atomic_operation
indexed_values = Util.Array(@field.to_indexed(value))
indexed_values = [nil] if indexed_values.empty? && atomic_operation
indexed_values.each do |scalar_value|
field_options = {}
field_options[:boost] = @field.boost if @field.boost
field_options[:null] = true if scalar_value.nil? && atomic_operation
document.add_field(
@field.indexed_name.to_sym,
scalar_value,
field_options.merge(options)
)
end
end
end
#
# A unique signature identifying this field by name and type.
#
def signature
[@field.name, @field.type]
end
end
class Join < Abstract
def initialize(name, type, options = {}, &block)
super(options[:prefix] ? "#{options[:prefix]}_#{name}" : name, options, &block)
unless name.to_s =~ /^\w+$/
raise ArgumentError, "Invalid field name #{name}: only letters, numbers, and underscores are allowed."
end
@field = JoinField.new(self.name, type, options)
end
#
# Return the field instance built by this factory
#
def build
@field
end
#
# A unique signature identifying this field by name and type.
#
def signature
['join', @field.name, @field.type]
end
end
#
# DynamicFieldFactories create dynamic field instances based on dynamic
# configuration.
#
class Dynamic < Abstract
attr_accessor :name, :type, :separator
def initialize(name, type, options = {}, &block)
super(name, options, &block)
@type, @options = type, options
@separator = @options.delete(:separator) || ':'
end
#
# Build a field based on the dynamic name given.
#
def build(dynamic_name)
AttributeField.new([@name, dynamic_name].join(separator), @type, @options.dup)
end
#
# This alias allows a DynamicFieldFactory to be used in place of a Setup
# or CompositeSetup instance by query components.
#
alias_method :field, :build
#
# Generate dynamic fields based on hash returned by data accessor and
# add the field data to the document.
#
def populate_document(document, model, options = {})
values = extract_value(model, options)
if values
values.each_pair do |dynamic_name, value|
field_instance = build(dynamic_name)
Util.Array(field_instance.to_indexed(value)).each do |scalar_value|
document.add_field(
field_instance.indexed_name.to_sym,
scalar_value,
options
)
end
end
end
end
#
# Unique signature identifying this dynamic field based on name and type
#
def signature
[@name, @type]
end
end
end
end