mebla 1.0.0.rc2
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/.document +6 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +79 -0
- data/LICENSE.txt +20 -0
- data/README.md +250 -0
- data/Rakefile +57 -0
- data/TODO.md +12 -0
- data/VERSION +1 -0
- data/lib/generators/mebla/install/USAGE +7 -0
- data/lib/generators/mebla/install/install_generator.rb +46 -0
- data/lib/generators/mebla/install/templates/mebla.yml +15 -0
- data/lib/mebla/configuration.rb +73 -0
- data/lib/mebla/context.rb +225 -0
- data/lib/mebla/errors/mebla_configuration_exception.rb +10 -0
- data/lib/mebla/errors/mebla_error.rb +14 -0
- data/lib/mebla/errors/mebla_fatal.rb +14 -0
- data/lib/mebla/errors/mebla_index_exception.rb +10 -0
- data/lib/mebla/errors/mebla_synchronization_exception.rb +9 -0
- data/lib/mebla/log_subscriber.rb +77 -0
- data/lib/mebla/mongoid/mebla.rb +258 -0
- data/lib/mebla/railtie.rb +38 -0
- data/lib/mebla/result_set.rb +85 -0
- data/lib/mebla/tasks.rb +42 -0
- data/lib/mebla.rb +137 -0
- data/mebla.gemspec +248 -0
- data/spec/fixtures/models.rb +37 -0
- data/spec/fixtures/mongoid.yml +3 -0
- data/spec/mebla/indexing_spec.rb +63 -0
- data/spec/mebla/searching_spec.rb +73 -0
- data/spec/mebla/synchronization_spec.rb +45 -0
- data/spec/mebla_helper.rb +33 -0
- data/spec/mebla_spec.rb +27 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/support/mongoid.rb +3 -0
- data/spec/support/rails.rb +13 -0
- metadata +696 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
# @private
|
2
|
+
module Mebla
|
3
|
+
# Handles indexing and reindexing
|
4
|
+
class Context
|
5
|
+
attr_reader :indexed_models, :slingshot_index, :slingshot_index_name
|
6
|
+
attr_reader :mappings
|
7
|
+
|
8
|
+
# @private
|
9
|
+
def initialize
|
10
|
+
@indexed_models = []
|
11
|
+
@mappings = {}
|
12
|
+
@slingshot_index = Slingshot::Index.new(Mebla::Configuration.instance.index)
|
13
|
+
@slingshot_index_name = Mebla::Configuration.instance.index
|
14
|
+
end
|
15
|
+
|
16
|
+
# @private
|
17
|
+
# Adds a model to the list of indexed models
|
18
|
+
def add_indexed_model(model, mappings = {})
|
19
|
+
model = model.name if model.is_a?(Class)
|
20
|
+
|
21
|
+
@indexed_models << model
|
22
|
+
@indexed_models.uniq!
|
23
|
+
@indexed_models.sort!
|
24
|
+
|
25
|
+
@mappings.merge!(mappings)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Deletes and rebuilds the index
|
29
|
+
# @note Doesn't index the data, use Mebla::Context#reindex_data to rebuild the index and index the data
|
30
|
+
# @return [nil]
|
31
|
+
def rebuild_index
|
32
|
+
# Only rebuild if the index exists
|
33
|
+
raise ::Mebla::Errors::MeblaIndexException.new("#{@slingshot_index_name} does not exist !! use #create_index to create the index first.") unless index_exists?
|
34
|
+
|
35
|
+
::Mebla.log("Rebuilding index")
|
36
|
+
|
37
|
+
# Delete the index
|
38
|
+
if drop_index
|
39
|
+
# Create the index
|
40
|
+
return build_index
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Creates and indexes the document
|
45
|
+
# @note Doesn't index the data, use Mebla::Context#index_data to create the index and index the data
|
46
|
+
# @return [Boolean] true if operation is successful
|
47
|
+
def create_index
|
48
|
+
# Only create the index if it doesn't exist
|
49
|
+
raise ::Mebla::Errors::MeblaIndexException.new("#{@slingshot_index_name} already exists !! use #rebuild_index to rebuild the index.") if index_exists?
|
50
|
+
|
51
|
+
::Mebla.log("Creating index")
|
52
|
+
|
53
|
+
# Create the index
|
54
|
+
build_index
|
55
|
+
end
|
56
|
+
|
57
|
+
# Deletes the index of the document
|
58
|
+
# @return [Boolean] true if operation is successful
|
59
|
+
def drop_index
|
60
|
+
# Only drop the index if it exists
|
61
|
+
return true unless index_exists?
|
62
|
+
|
63
|
+
::Mebla.log("Dropping index: #{self.slingshot_index_name}", :debug)
|
64
|
+
|
65
|
+
# Drop the index
|
66
|
+
result = @slingshot_index.delete
|
67
|
+
|
68
|
+
::Mebla.log("Dropped #{self.slingshot_index_name}: #{result.to_s}", :debug)
|
69
|
+
|
70
|
+
# Check that the index doesn't exist
|
71
|
+
!index_exists?
|
72
|
+
end
|
73
|
+
|
74
|
+
# Checks if the index exists and is available
|
75
|
+
# @return [Boolean] true if the index exists and is available, false otherwise
|
76
|
+
def index_exists?
|
77
|
+
begin
|
78
|
+
result = Slingshot::Configuration.client.get "#{Mebla::Configuration.instance.url}/#{@slingshot_index_name}/_status"
|
79
|
+
return (result =~ /error/) ? false : true
|
80
|
+
rescue RestClient::ResourceNotFound
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates the index and indexes the data for all models or a list of models given
|
86
|
+
# @param *models a list of symbols each representing a model name to be indexed
|
87
|
+
# @return [nil]
|
88
|
+
def index_data(*models)
|
89
|
+
if models.empty?
|
90
|
+
only_index = @indexed_models
|
91
|
+
else
|
92
|
+
only_index = models.collect{|m| m.to_s}
|
93
|
+
end
|
94
|
+
|
95
|
+
::Mebla.log("Indexing #{only_index.join(", ")}", :debug)
|
96
|
+
|
97
|
+
# Build up a bulk query to save processing and time
|
98
|
+
bulk_query = ""
|
99
|
+
# Keep track of indexed documents
|
100
|
+
indexed_count = {}
|
101
|
+
|
102
|
+
# Create the index
|
103
|
+
if create_index
|
104
|
+
# Start collecting documents
|
105
|
+
only_index.each do |model|
|
106
|
+
::Mebla.log("Indexing: #{model}")
|
107
|
+
# Get the class
|
108
|
+
to_index = model.camelize.constantize
|
109
|
+
|
110
|
+
# Get the records
|
111
|
+
entries = []
|
112
|
+
unless to_index.embedded?
|
113
|
+
entries = to_index.all.only(to_index.search_fields)
|
114
|
+
else
|
115
|
+
parent = to_index.embedded_parent
|
116
|
+
access_method = to_index.embedded_as
|
117
|
+
|
118
|
+
parent.all.each do |parent_record|
|
119
|
+
entries += parent_record.send(access_method.to_sym).all.only(to_index.search_fields)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Save the number of entries to be indexed
|
124
|
+
indexed_count[model] = entries.count
|
125
|
+
|
126
|
+
# Build the queries for this model
|
127
|
+
entries.each do |document|
|
128
|
+
attrs = document.attributes.dup # make sure we dont modify the document it self
|
129
|
+
attrs["id"] = attrs.delete("_id") # the id is already added in the meta data of the action part of the query
|
130
|
+
|
131
|
+
if document.embedded?
|
132
|
+
parent_id = document.send(document.class.embedded_parent_foreign_key.to_sym).id.to_s
|
133
|
+
attrs[(document.class.embedded_parent_foreign_key + "_id").to_sym] = parent_id
|
134
|
+
|
135
|
+
# Build add to the bulk query
|
136
|
+
bulk_query << build_bulk_query(@slingshot_index_name, to_index.slingshot_type_name, document.id.to_s, attrs, parent_id)
|
137
|
+
else
|
138
|
+
# Build add to the bulk query
|
139
|
+
bulk_query << build_bulk_query(@slingshot_index_name, to_index.slingshot_type_name, document.id.to_s, attrs)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
else
|
144
|
+
raise ::Mebla::Errors::MeblaIndexException.new("Could not create #{@slingshot_index_name}!!!")
|
145
|
+
end
|
146
|
+
|
147
|
+
# Add a new line to the query
|
148
|
+
bulk_query << '\n'
|
149
|
+
|
150
|
+
::Mebla.log("Bulk indexing:\n#{bulk_query}", :debug)
|
151
|
+
|
152
|
+
# Send the query
|
153
|
+
response = Slingshot::Configuration.client.post "#{Mebla::Configuration.instance.url}/_bulk", bulk_query
|
154
|
+
|
155
|
+
# Only refresh the index if no error ocurred
|
156
|
+
unless response =~ /error/
|
157
|
+
# Log results
|
158
|
+
::Mebla.log("Indexed #{only_index.count} model(s) to #{self.slingshot_index_name}: #{response}")
|
159
|
+
::Mebla.log("Indexing Report:")
|
160
|
+
indexed_count.each do |model_name, count|
|
161
|
+
::Mebla.log("Indexed #{model_name}: #{count} document(s)")
|
162
|
+
end
|
163
|
+
|
164
|
+
# Refresh the index
|
165
|
+
refresh_index
|
166
|
+
else
|
167
|
+
raise ::Mebla::Errors::MeblaIndexException.new("Indexing #{only_index.join(", ")} failed with the following response:\n #{response}")
|
168
|
+
end
|
169
|
+
rescue RestClient::Exception => error
|
170
|
+
raise ::Mebla::Errors::MeblaIndexException.new("Indexing #{only_index.join(", ")} failed with the following error: #{error.message}")
|
171
|
+
end
|
172
|
+
|
173
|
+
# Rebuilds the index and indexes the data for all models or a list of models given
|
174
|
+
# @param *models a list of symbols each representing a model name to rebuild it's index
|
175
|
+
# @return [nil]
|
176
|
+
def reindex_data(*models)
|
177
|
+
::Mebla.log("Rendexing: #{self.slingshot_index_name}")
|
178
|
+
|
179
|
+
unless drop_index
|
180
|
+
raise ::Mebla::Errors::MeblaIndexException.new("Could not drop #{@slingshot_index_name}!!!")
|
181
|
+
end
|
182
|
+
|
183
|
+
# Create the index and index the data
|
184
|
+
index_data(models)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Refreshes the index
|
188
|
+
# @return [nil]
|
189
|
+
def refresh_index
|
190
|
+
::Mebla.log("Refreshing: #{self.slingshot_index_name}", :debug)
|
191
|
+
|
192
|
+
result = @slingshot_index.refresh
|
193
|
+
|
194
|
+
::Mebla.log("Refreshed #{self.slingshot_index_name}: #{result}")
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
# Builds the index according to the mappings set
|
199
|
+
# @return [Boolean] true if the index was created successfully, false otherwise
|
200
|
+
def build_index
|
201
|
+
::Mebla.log("Building index", :debug)
|
202
|
+
# Create the index
|
203
|
+
result = @slingshot_index.create :mappings => @mappings
|
204
|
+
|
205
|
+
::Mebla.log("Created index: #{result.to_s}")
|
206
|
+
|
207
|
+
# Check if the index exists
|
208
|
+
index_exists?
|
209
|
+
end
|
210
|
+
|
211
|
+
# --
|
212
|
+
# OPTIMIZE: should find a solution for not refreshing the index while indexing embedded documents
|
213
|
+
# ++
|
214
|
+
|
215
|
+
# Builds a bulk index query
|
216
|
+
# @return [String]
|
217
|
+
def build_bulk_query(index_name, type, id, attributes, parent = nil)
|
218
|
+
attrs_to_json = attributes.collect{|k,v| "\"#{k}\" : \"#{v}\""}.join(", ")
|
219
|
+
<<-eos
|
220
|
+
{ "index" : { "_index" : "#{index_name}", "_type" : "#{type}", "_id" : "#{id}"#{", \"_parent\" : \"#{parent}\"" if parent}, "refresh" : "true"} }
|
221
|
+
{#{attrs_to_json}}
|
222
|
+
eos
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# @private
|
2
|
+
module Mebla
|
3
|
+
# @private
|
4
|
+
module Errors
|
5
|
+
# Default parent Mebla error for all custom non-fatal errors.
|
6
|
+
class MeblaError < ::StandardError
|
7
|
+
def initialize(message)
|
8
|
+
super message
|
9
|
+
::ActiveSupport::Notifications.
|
10
|
+
instrument('mebla_error.mebla', :message => message)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# @private
|
2
|
+
module Mebla
|
3
|
+
# @private
|
4
|
+
module Errors
|
5
|
+
# Default parent Mebla error for all custom fatal errors.
|
6
|
+
class MeblaFatal < ::StandardError
|
7
|
+
def initialize(message)
|
8
|
+
super message
|
9
|
+
::ActiveSupport::Notifications.
|
10
|
+
instrument('mebla_fatal.mebla', :message => message)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'active_support/log_subscriber'
|
2
|
+
|
3
|
+
# @private
|
4
|
+
module Mebla
|
5
|
+
# Handles logging
|
6
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
7
|
+
# Debug message
|
8
|
+
def mebla_debug(event)
|
9
|
+
debug_green event.payload[:message]
|
10
|
+
end
|
11
|
+
|
12
|
+
# Error message
|
13
|
+
def mebla_error(event)
|
14
|
+
error_red event.payload[:message]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Info message
|
18
|
+
def mebla_info(event)
|
19
|
+
info_blue event.payload[:message]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Fatal message
|
23
|
+
def mebla_fatal(event)
|
24
|
+
fatal_magenta event.payload[:message]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Warning message
|
28
|
+
def mebla_warn(event)
|
29
|
+
warn_yellow event.payload[:message]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Unkown message
|
33
|
+
def mebla_unkown(event)
|
34
|
+
unkown event.payload[:message]
|
35
|
+
end
|
36
|
+
|
37
|
+
# --
|
38
|
+
# -------------------------------------------------------------
|
39
|
+
# Add some colors
|
40
|
+
# -------------------------------------------------------------
|
41
|
+
# ++
|
42
|
+
|
43
|
+
# Print a debug message to the log file
|
44
|
+
def debug_green(msg)
|
45
|
+
debug color(msg, LogSubscriber::Green)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Print an error message to the log file
|
49
|
+
def error_red(msg)
|
50
|
+
error color(msg, LogSubscriber::RED)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Print an info message to the log file
|
54
|
+
def info_blue(msg)
|
55
|
+
ingo color(msg, LogSubscriber::BLUE)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Print a fatal message to the log file
|
59
|
+
def fatal_magenta(msg)
|
60
|
+
fatal color(msg, LogSubscriber::MAGENTA)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Print a warn message to the log file
|
64
|
+
def warn_yellow(msg)
|
65
|
+
warn color(msg, LogSubscriber::YELLOW)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the main logger for Mebla
|
69
|
+
# @return [Logger]
|
70
|
+
def logger
|
71
|
+
Mebla::Configuration.instance.logger
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Register the logger
|
77
|
+
Mebla::LogSubscriber.attach_to :mebla
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# @private
|
2
|
+
module Mongoid
|
3
|
+
# --
|
4
|
+
# TODO: add ability to index embedded documents (as part of the parent document)
|
5
|
+
# ++
|
6
|
+
|
7
|
+
# A wrapper for slingshot elastic-search adapter for Mongoid
|
8
|
+
module Mebla
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
included do
|
11
|
+
# Used to properly represent data types
|
12
|
+
unless defined?(SLINGSHOT_TYPE_MAPPING)
|
13
|
+
SLINGSHOT_TYPE_MAPPING = {
|
14
|
+
'Array' => 'array',
|
15
|
+
'Date' => 'date',
|
16
|
+
'DateTime' => 'date',
|
17
|
+
'Time' => 'date',
|
18
|
+
'Float' => 'float',
|
19
|
+
'Integer' => 'integer',
|
20
|
+
'BigDecimal' => 'float',
|
21
|
+
'Boolean' => 'boolean'
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
cattr_accessor :embedded_as
|
26
|
+
cattr_accessor :embedded_parent
|
27
|
+
cattr_accessor :embedded_parent_foreign_key
|
28
|
+
cattr_accessor :index_mappings
|
29
|
+
cattr_accessor :index_options
|
30
|
+
cattr_accessor :search_fields
|
31
|
+
cattr_accessor :whiny_indexing # set to true to raise errors if indexing fails
|
32
|
+
|
33
|
+
# make sure critical data remain read only
|
34
|
+
private_class_method :"search_fields=", :"index_options=", :"index_mappings=",
|
35
|
+
:"embedded_parent_foreign_key=", :"embedded_parent=", :"embedded_as="
|
36
|
+
|
37
|
+
# add callbacks to synchronize modifications with elasticsearch
|
38
|
+
after_save :add_to_index
|
39
|
+
before_destroy :remove_from_index
|
40
|
+
|
41
|
+
# by default if synchronizing fails no error is raised
|
42
|
+
self.whiny_indexing = false
|
43
|
+
end
|
44
|
+
|
45
|
+
module ClassMethods
|
46
|
+
# Defines which fields should be indexed and searched
|
47
|
+
# @param [*opts] fields
|
48
|
+
# @return [nil]
|
49
|
+
#
|
50
|
+
# Defines a search index on a normal document with custom mappings on "body"::
|
51
|
+
#
|
52
|
+
# class Document
|
53
|
+
# include Mongoid::Document
|
54
|
+
# include Mongoid::Mebla
|
55
|
+
# field :title
|
56
|
+
# field :body
|
57
|
+
# field :publish_date, :type => Date
|
58
|
+
# #...
|
59
|
+
# search_in :title, :publish_date, :body => { :boost => 2.0, :analyzer => 'snowball' }
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# Defines a search index on an embedded document with a single parent and custom mappings on "body"::
|
63
|
+
#
|
64
|
+
# class Document
|
65
|
+
# include Mongoid::Document
|
66
|
+
# include Mongoid::Mebla
|
67
|
+
# field :title
|
68
|
+
# field :body
|
69
|
+
# field :publish_date, :type => Date
|
70
|
+
# #...
|
71
|
+
# embedded_in :category
|
72
|
+
# search_in :title, :publish_date, :body => { :boost => 2.0, :analyzer => 'snowball' }, :embedded_in => :category
|
73
|
+
# end
|
74
|
+
def search_in(*opts)
|
75
|
+
# Extract advanced indeces
|
76
|
+
options = opts.extract_options!.symbolize_keys
|
77
|
+
# Extract simple indeces
|
78
|
+
attrs = opts.flatten
|
79
|
+
|
80
|
+
|
81
|
+
# If this document is embedded check for the embedded_in option and raise an error if none is specified
|
82
|
+
# Example::
|
83
|
+
# embedded in a regular class (e.g.: using the default convention for naming the foreign key)
|
84
|
+
# :embedded_in => :parent
|
85
|
+
if self.embedded?
|
86
|
+
if (embedor = options.delete(:embedded_in))
|
87
|
+
relation = self.relations[embedor.to_s]
|
88
|
+
|
89
|
+
# Infer the attributes of the relation
|
90
|
+
self.embedded_parent = relation.class_name.constantize
|
91
|
+
self.embedded_parent_foreign_key = relation.key.to_s
|
92
|
+
self.embedded_as = relation[:inverse_of] || relation.inverse_setter.to_s.gsub(/=$/, '')
|
93
|
+
|
94
|
+
if self.embedded_as.blank?
|
95
|
+
raise ::Mebla::Errors::MeblaConfigurationException.new("Couldn't infer #{embedor.to_s} inverse relation, please set :inverse_of option on the relation.")
|
96
|
+
end
|
97
|
+
else
|
98
|
+
raise ::Mebla::Errors::MeblaConfigurationException.new("#{self.model_name} is embedded: embedded_in option should be set to the parent class if the document is embedded.")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Keep track of searchable fields (for indexing)
|
103
|
+
self.search_fields = attrs + options.keys
|
104
|
+
|
105
|
+
# Generate simple indeces' mappings
|
106
|
+
attrs_mappings = {}
|
107
|
+
|
108
|
+
attrs.each do |attribute|
|
109
|
+
attrs_mappings[attribute] = {:type => SLINGSHOT_TYPE_MAPPING[self.fields[attribute.to_s].type.to_s] || "string"}
|
110
|
+
end
|
111
|
+
|
112
|
+
# Generate advanced indeces' mappings
|
113
|
+
opts_mappings = {}
|
114
|
+
|
115
|
+
options.each do |opt, properties|
|
116
|
+
opts_mappings[opt] = {:type => SLINGSHOT_TYPE_MAPPING[self.fields[opt.to_s].type.to_s] || "string" }.merge!(properties)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Merge mappings
|
120
|
+
self.index_mappings = {}.merge!(attrs_mappings).merge!(opts_mappings)
|
121
|
+
|
122
|
+
# Keep track of indexed models (for bulk indexing)
|
123
|
+
::Mebla.context.add_indexed_model(self, self.slingshot_type_name.to_sym => prepare_mappings)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Searches the model using Slingshot search DSL
|
127
|
+
# @return [ResultSet]
|
128
|
+
#
|
129
|
+
# Search for posts with the title 'Testing Search'::
|
130
|
+
#
|
131
|
+
# Post.search do
|
132
|
+
# query do
|
133
|
+
# string "title: Testing Search"
|
134
|
+
# end
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# @note For more information about Slingshot search DSL, check http://karmi.github.com/slingshot
|
138
|
+
def search(&block)
|
139
|
+
::Mebla.search(self.slingshot_type_name, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Retrieves the type name of the model
|
143
|
+
# (used to populate the _type variable while indexing)
|
144
|
+
# @return [String]
|
145
|
+
def slingshot_type_name #:nodoc:
|
146
|
+
"#{self.model_name.underscore}"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Enables the modification of records without indexing
|
150
|
+
# @return [nil]
|
151
|
+
# Example::
|
152
|
+
# create record without it being indexed
|
153
|
+
# Class.without_indexing do
|
154
|
+
# create :title => "This is not indexed", :body => "Nothing will be indexed within this block"
|
155
|
+
# end
|
156
|
+
# @note you can skip indexing to create, update or delete records without affecting the index
|
157
|
+
def without_indexing(&block)
|
158
|
+
skip_callback(:save, :after, :add_to_index)
|
159
|
+
skip_callback(:destroy, :before, :remove_from_index)
|
160
|
+
yield
|
161
|
+
set_callback(:save, :after, :add_to_index)
|
162
|
+
set_callback(:destroy, :before, :remove_from_index)
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
# Prepare the mappings required for this document
|
167
|
+
# @return [Hash]
|
168
|
+
def prepare_mappings
|
169
|
+
if self.embedded?
|
170
|
+
mappings = {
|
171
|
+
:_parent => { :type => self.embedded_parent.name.underscore },
|
172
|
+
:_routing => {
|
173
|
+
:required => true,
|
174
|
+
:path => self.embedded_parent_foreign_key + "_id"
|
175
|
+
}
|
176
|
+
}
|
177
|
+
else
|
178
|
+
mappings = {}
|
179
|
+
end
|
180
|
+
|
181
|
+
mappings.merge!({
|
182
|
+
:properties => self.index_mappings
|
183
|
+
})
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
# Adds the document to the index
|
189
|
+
# @return [Boolean] true if the operation is successful
|
190
|
+
def add_to_index
|
191
|
+
return false unless ::Mebla.context.index_exists? # only try to index if the index exists
|
192
|
+
|
193
|
+
# Prepare attributes to hash
|
194
|
+
to_index_hash = {:id => self.id.to_s}
|
195
|
+
|
196
|
+
# If the document is embedded set _parent to the parent's id
|
197
|
+
if self.embedded?
|
198
|
+
parent_id = self.send(self.class.embedded_parent_foreign_key.to_sym).id.to_s
|
199
|
+
to_index_hash.merge!({
|
200
|
+
(self.class.embedded_parent_foreign_key + "_id").to_sym => parent_id,
|
201
|
+
:_parent => parent_id
|
202
|
+
})
|
203
|
+
end
|
204
|
+
|
205
|
+
# Add indexed fields to the hash
|
206
|
+
self.search_fields.each do |sfield|
|
207
|
+
to_index_hash[sfield] = self.attributes[sfield]
|
208
|
+
end
|
209
|
+
|
210
|
+
::Mebla.log("Indexing #{self.class.slingshot_type_name}: #{to_index_hash.to_s}", :debug)
|
211
|
+
|
212
|
+
# Index the data under its correct type
|
213
|
+
response = ::Mebla.context.slingshot_index.store(self.class.slingshot_type_name.to_sym, to_index_hash)
|
214
|
+
|
215
|
+
::Mebla.log("Response for indexing #{self.class.slingshot_type_name}: #{response.to_s}", :debug)
|
216
|
+
|
217
|
+
# Refresh the index
|
218
|
+
::Mebla.context.refresh_index
|
219
|
+
return true
|
220
|
+
rescue => error
|
221
|
+
raise_synchronization_exception(error)
|
222
|
+
|
223
|
+
return false
|
224
|
+
end
|
225
|
+
|
226
|
+
# Deletes the document from the index
|
227
|
+
# @return [Boolean] true if the operation is successful
|
228
|
+
def remove_from_index
|
229
|
+
return false unless ::Mebla.context.index_exists? # only try to index if the index exists
|
230
|
+
|
231
|
+
::Mebla.log("Removing #{self.class.slingshot_type_name} with id: #{self.id.to_s}", :debug)
|
232
|
+
|
233
|
+
# Delete the document
|
234
|
+
response = Slingshot::Configuration.client.delete "#{::Mebla::Configuration.instance.url}/#{::Mebla.context.slingshot_index_name}/#{self.class.slingshot_type_name}/#{self.id.to_s}"
|
235
|
+
|
236
|
+
::Mebla.log("Response for removing #{self.class.slingshot_type_name}: #{response.to_s}", :debug)
|
237
|
+
|
238
|
+
# Refresh the index
|
239
|
+
::Mebla.context.refresh_index
|
240
|
+
return true
|
241
|
+
rescue => error
|
242
|
+
raise_synchronization_exception(error)
|
243
|
+
|
244
|
+
return false
|
245
|
+
end
|
246
|
+
|
247
|
+
def raise_synchronization_exception(error)
|
248
|
+
exception_message = "#{self.class.slingshot_type_name} synchronization failed with the following error: #{error.message}"
|
249
|
+
if self.class.whiny_indexing
|
250
|
+
# Whine when mebla is not able to synchronize
|
251
|
+
raise ::Mebla::Errors::MeblaSynchronizationException.new(exception_message)
|
252
|
+
else
|
253
|
+
# Whining is not allowed, silently log the exception
|
254
|
+
::Mebla.log(exception_message, :warn)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'mebla'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
# @private
|
5
|
+
module Mebla
|
6
|
+
# @private
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
# Configuration
|
9
|
+
initializer "mebla.set_configs" do |app|
|
10
|
+
Mebla.configure do |config|
|
11
|
+
# Open logfile
|
12
|
+
config.logger = Logger.new(
|
13
|
+
open("#{Dir.pwd}/logs/#{Rails.env}.mebla.log", "a")
|
14
|
+
)
|
15
|
+
# Setup the log level
|
16
|
+
config.logger.level = case app.config.log_level
|
17
|
+
when :info
|
18
|
+
Logger::INFO
|
19
|
+
when :warn
|
20
|
+
Logger::WARN
|
21
|
+
when :error
|
22
|
+
Logger::ERROR
|
23
|
+
when :fatal
|
24
|
+
Logger::FATAL
|
25
|
+
else
|
26
|
+
Logger::DEBUG
|
27
|
+
end
|
28
|
+
|
29
|
+
config.setup_logger
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Rake tasks
|
34
|
+
rake_tasks do
|
35
|
+
load File.expand_path('../tasks.rb', __FILE__)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|