Class: Mebla::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/mebla/context.rb

Overview

Handles indexing and reindexing

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeContext

Creates a new context object



10
11
12
13
14
15
# File 'lib/mebla/context.rb', line 10

def initialize            
  @indexed_models = []
  @mappings = {}
  @slingshot_index = Slingshot::Index.new(Mebla::Configuration.instance.index)
  @slingshot_index_name = Mebla::Configuration.instance.index
end

Instance Attribute Details

#indexed_modelsObject (readonly)

Returns the value of attribute indexed_models.



5
6
7
# File 'lib/mebla/context.rb', line 5

def indexed_models
  @indexed_models
end

#mappingsObject (readonly)

Returns the value of attribute mappings.



6
7
8
# File 'lib/mebla/context.rb', line 6

def mappings
  @mappings
end

#slingshot_indexObject (readonly)

Returns the value of attribute slingshot_index.



5
6
7
# File 'lib/mebla/context.rb', line 5

def slingshot_index
  @slingshot_index
end

#slingshot_index_nameObject (readonly)

Returns the value of attribute slingshot_index_name.



5
6
7
# File 'lib/mebla/context.rb', line 5

def slingshot_index_name
  @slingshot_index_name
end

Instance Method Details

#add_indexed_model(model, mappings = {}) ⇒ Object

Adds a model to the list of indexed models



19
20
21
22
23
24
25
26
27
# File 'lib/mebla/context.rb', line 19

def add_indexed_model(model, mappings = {})
  model = model.name if model.is_a?(Class)
  
  @indexed_models << model
  @indexed_models.uniq!
  @indexed_models.sort!
  
  @mappings.merge!(mappings)
end

#create_indexBoolean

Note:

Doesn’t index the data, use Mebla::Context#index_data to create the index and index the data

Creates and indexes the document

Returns:

  • (Boolean)

    true if operation is successful

Raises:



48
49
50
51
52
53
54
55
56
# File 'lib/mebla/context.rb', line 48

def create_index
  # Only create the index if it doesn't exist
  raise Mebla::Errors::MeblaIndexException.new("#{@slingshot_index_name} already exists !! use #rebuild_index to rebuild the index.") if index_exists?
  
  Mebla.log("Creating index")
  
  # Create the index
  build_index
end

#drop_indexBoolean

Deletes the index of the document

Returns:

  • (Boolean)

    true if operation is successful



60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/mebla/context.rb', line 60

def drop_index
  # Only drop the index if it exists
  return true unless index_exists?
  
  Mebla.log("Dropping index: #{self.slingshot_index_name}", :debug)
  
  # Drop the index
  result = @slingshot_index.delete
  
  Mebla.log("Dropped #{self.slingshot_index_name}: #{result.to_s}", :debug)
  
  # Check that the index doesn't exist
  !index_exists?
end

#index_data(*models) ⇒ nil

Creates the index and indexes the data for all models or a list of models given

Parameters:

  • *models

    a list of symbols each representing a model name to be indexed

Returns:

  • (nil)


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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/mebla/context.rb', line 89

def index_data(*models)
  if models.nil? || models.empty?
    only_index = @indexed_models
  else
    only_index = models.collect{|m| m.to_s}
  end      
  
  Mebla.log("Indexing #{only_index.join(", ")}", :debug)
  
  # Build up a bulk query to save processing and time
  bulk_query = ""
  # Keep track of indexed documents
  indexed_count = {}
  
  # Create the index
  if create_index
    # Start collecting documents
    only_index.each do |model|
      Mebla.log("Indexing: #{model}")
      # Get the class
      to_index = model.camelize.constantize
      
      # Get the records    
      entries = []
      unless to_index.embedded?
        if to_index.sub_class?
          entries = to_index.any_in(:_type => [to_index.name])
        else              
          entries = to_index.any_in(:_type => [nil, to_index.name])
        end
      else
        parent = to_index.embedded_parent
        access_method = to_index.embedded_as
        
        parent.all.each do |parent_record|
          if to_index.sub_class?
            entries += parent_record.send(access_method.to_sym).any_in(:_type => [to_index.name])
          else
            entries += parent_record.send(access_method.to_sym).any_in(:_type => [nil, to_index.name])
          end
        end
      end
      
      # Save the number of entries to be indexed
      indexed_count[model] = entries.count          
      
      # Build the queries for this model          
      entries.each do |document|
        attrs = {} #document.attributes.dup # make sure we dont modify the document it self
        attrs[:id] = document.attributes["_id"] # the id is already added in the meta data of the action part of the query
        
        # only index search fields  and methods
        document.class.search_fields.each do |field|
          if document.attributes.keys.include?(field.to_s)
            attrs[field] = document.attributes[field.to_s] # attribute
          else
            attrs[field] = document.send(field) # method
          end
        end
        
        # index relational fields
        document.class.search_relations.each do |relation, fields|              
          items = document.send(relation.to_sym) # get the relation document
          
          next if items.nil?
          
          # N relation side
          if items.is_a?(Array) || items.is_a?(Mongoid::Relations::Targets::Enumerable)
            next if items.empty?
            attrs[relation] = []
            items.each do |item|
              if fields.is_a?(Array) # given multiple fields to index
                fields_values = {}
                fields.each do |field|
                  if item.attributes.keys.include?(field.to_s)
                    fields_values.merge!({ field => item.attributes[field.to_s] }) # attribute
                  else
                    fields_values.merge!({ field => item.send(field) }) # method
                  end
                end
                attrs[relation] << fields_values
              else # only index one field in the relation
                if item.attributes.keys.include?(fields.to_s)
                  attrs[relation] << { fields => item.attributes[fields.to_s] } # attribute
                else
                  attrs[relation] << { fields => item.send(fields) } # method
                end
              end
            end
          # 1 relation side
          else
            attrs[relation] = {}
            if fields.is_a?(Array) # given multiple fields to index
              fields_values = {}
              fields.each do |field|
                if items.attributes.keys.include?(field.to_s)
                  fields_values.merge!({ field => items.attributes[field.to_s] }) # attribute
                else
                  fields_values.merge!({ field => items.send(field) }) # method
                end
              end
              attrs[relation].merge!(fields_values)
            else # only index one field in the relation
              if items.attributes.keys.include?(fields.to_s)
                attrs[relation].merge!({ fields => items.attributes[fields.to_s] }) # attribute
              else
                attrs[relation].merge!({ fields => items.send(fields) }) # method
              end
            end
          end
        end  
        
        # If embedded get the parent id
        if document.embedded?
          parent_id = document.send(document.class.embedded_parent_foreign_key.to_sym).id.to_s        
          attrs[(document.class.embedded_parent_foreign_key + "_id").to_sym] = parent_id
          attrs[:_parent] = parent_id
          
          # Build add to the bulk query
          bulk_query << build_bulk_query(@slingshot_index_name, to_index.slingshot_type_name, document.id.to_s, attrs, parent_id)
        else
          # Build add to the bulk query
          bulk_query << build_bulk_query(@slingshot_index_name, to_index.slingshot_type_name, document.id.to_s, attrs)
        end
      end
    end
  else
    raise Mebla::Errors::MeblaIndexException.new("Could not create #{@slingshot_index_name}!!!")
  end      
  
  Mebla.log("Bulk indexing:\n#{bulk_query}", :debug)      
  
  # Send the query
  response = Slingshot::Configuration.client.post "#{Mebla::Configuration.instance.url}/_bulk", bulk_query
  
  # Only refresh the index if no error ocurred
  unless response =~ /error/                              
    # Log results
    Mebla.log("Indexed #{only_index.count} model(s) to #{self.slingshot_index_name}: #{response}")
    Mebla.log("Indexing Report:")
    indexed_count.each do |model_name, count|
      Mebla.log("Indexed #{model_name}: #{count} document(s)")
    end
    
    # Refresh the index
    refresh_index
  else
    raise Mebla::Errors::MeblaIndexException.new("Indexing #{only_index.join(", ")} failed with the following response:\n #{response}")
  end
rescue RestClient::Exception => error
  raise Mebla::Errors::MeblaIndexException.new("Indexing #{only_index.join(", ")} failed with the following error: #{error.message}")
end

#index_exists?Boolean

Checks if the index exists and is available

Returns:

  • (Boolean)

    true if the index exists and is available, false otherwise



77
78
79
80
81
82
83
84
# File 'lib/mebla/context.rb', line 77

def index_exists?
  begin
    result = Slingshot::Configuration.client.get "#{Mebla::Configuration.instance.url}/#{@slingshot_index_name}/_status"
    return (result =~ /error/) ? false : true
  rescue RestClient::ResourceNotFound
    return false
  end
end

#rebuild_indexnil

Note:

Doesn’t index the data, use Mebla::Context#reindex_data to rebuild the index and index the data

Deletes and rebuilds the index

Returns:

  • (nil)

Raises:



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/mebla/context.rb', line 32

def rebuild_index
  # Only rebuild if the index exists
  raise Mebla::Errors::MeblaIndexException.new("#{@slingshot_index_name} does not exist !! use #create_index to create the index first.") unless index_exists?

  Mebla.log("Rebuilding index")      
  
  # Delete the index
  if drop_index
    # Create the index
    return build_index
  end
end

#refresh_indexnil

Refreshes the index

Returns:

  • (nil)


262
263
264
265
266
267
268
# File 'lib/mebla/context.rb', line 262

def refresh_index
  Mebla.log("Refreshing: #{self.slingshot_index_name}", :debug)
  
  result = @slingshot_index.refresh
  
  Mebla.log("Refreshed #{self.slingshot_index_name}: #{result}")
end

#reindex_data(*models) ⇒ nil

Rebuilds the index and indexes the data for all models or a list of models given

Parameters:

  • *models

    a list of symbols each representing a model name to rebuild it’s index

Returns:

  • (nil)


245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/mebla/context.rb', line 245

def reindex_data(*models)   
  Mebla.log("Rendexing: #{self.slingshot_index_name}")
  
  unless drop_index
    raise Mebla::Errors::MeblaIndexException.new("Could not drop #{@slingshot_index_name}!!!")
  end        
  
  # Create the index and index the data
  if models && !models.empty?
    index_data(models)
  else
    index_data
  end
end