module Jekyll::Algolia::Indexer
def self.delete_records_by_id(index, ids)
ids - Array of objectIDs to delete
index - Algolia Index to target
Public: Delete records whose objectIDs are passed
def self.delete_records_by_id(index, ids) return if ids.empty? Logger.log("I:Deleting #{ids.length} records") return if Configurator.dry_run? begin index.delete_objects!(ids) rescue StandardError => error ErrorHandler.stop(error) end end
def self.index(index_name)
Public: Returns an Algolia Index object from an index name
def self.index(index_name) ::Algolia::Index.new(index_name) end
def self.init
This call will instanciate the Algolia API client, set the custom
Public: Init the module
def self.init ::Algolia.init( application_id: Configurator.application_id, api_key: Configurator.api_key ) set_user_agent end
def self.local_object_ids(records)
Public: Returns an array of the local objectIDs
def self.local_object_ids(records) records.map { |record| record[:objectID] }.compact.sort end
def self.remote_object_ids(index)
The returned array is sorted. It won't have any impact on the way it is
index - Algolia Index to target
Public: Returns an array of all the objectIDs in the index
def self.remote_object_ids(index) list = [] begin index.browse(attributesToRetrieve: 'objectID') do |hit| list << hit['objectID'] end rescue StandardError # The index might not exist if it's the first time we use the plugin # so we'll consider that it means there are no records there return [] end list.sort end
def self.remote_settings(index)
Public: Get the settings of the remote index
def self.remote_settings(index) index.get_settings rescue StandardError => error ErrorHandler.stop(error) end
def self.rename_index(old_name, new_name)
new_name - New name of the index
old_name - Current name of the index
Public: Rename an index
def self.rename_index(old_name, new_name) Logger.verbose("I:Renaming `#{old_name}` to `#{new_name}`") return if Configurator.dry_run? begin ::Algolia.move_index(old_name, new_name) rescue StandardError => error ErrorHandler.stop(error, new_name: new_name) end end
def self.run(records)
Public: Push all records to Algolia and configure the index
def self.run(records) init record_count = records.length # Indexing zero record is surely a misconfiguration if record_count.zero? files_to_exclude = Configurator.algolia('files_to_exclude').join(', ') Logger.known_message( 'no_records_found', 'files_to_exclude' => files_to_exclude, 'nodes_to_index' => Configurator.algolia('nodes_to_index') ) exit 1 end indexing_mode = Configurator.indexing_mode Logger.verbose("I:Indexing mode: #{indexing_mode}") case indexing_mode when 'diff' run_diff_mode(records) when 'atomic' run_atomic_mode(records) end end
def self.run_atomic_mode(records)
consume more operations, but will never leave the index in a transient
people are always searching into a fully configured index. It will
For the end-user, it will make all the changes in one go, making sure
configure it, and then overwrite the previous index with this new one.
The `atomic` indexing mode will push all records to a brand new index,
records - Array of records to push
Public: Index content following the `atomic` indexing mode
def self.run_atomic_mode(records) index_name = Configurator.index_name index = index(index_name) index_tmp_name = "#{Configurator.index_name}_tmp" index_tmp = index(index_tmp_name) Logger.verbose("I:Using `#{index_tmp_name}` as temporary index") # Copying original settings to the new index remote_settings = remote_settings(index) new_settings = remote_settings.merge(Configurator.settings) update_settings(index_tmp, new_settings) # Pushing everthing to a brand new index update_records(index_tmp, records) # Renaming the new index in place of the old rename_index(index_tmp_name, index_name) Logger.log('I:✔ Indexing complete') end
def self.run_diff_mode(records)
updated. It will be a bit slower as it will first need to get the list
remove old content from it. It won't touch records that haven't been
The `diff` indexing mode will only push new content to the index and
records - Array of local records
Public: Index content following the `diff` indexing mode
def self.run_diff_mode(records) index = index(Configurator.index_name) # Update settings update_settings(index, Configurator.settings) # Getting list of objectID in remote and locally remote_ids = remote_object_ids(index) local_ids = local_object_ids(records) old_records_ids = remote_ids - local_ids new_records_ids = local_ids - remote_ids if old_records_ids.empty? && new_records_ids.empty? Logger.log('I:Nothing to index. Your content is already up to date.') return end Logger.log('I:Pushing records to Algolia...') # Delete remote records that are no longer available locally delete_records_by_id(index, old_records_ids) # Add only records that are not yet already in the remote new_records = records.select do |record| new_records_ids.include?(record[:objectID]) end update_records(index, new_records) Logger.log('I:✔ Indexing complete') end
def self.set_user_agent
each integration version is pinned to a specific API client version, we
every API client should follow the "Algolia for YYY" pattern. Even if
Every integrations should follow the "YYY Integration" pattern, and
Public: Set the User-Agent to send to the API
def self.set_user_agent user_agent = [ "Jekyll Integration (#{VERSION})", "Algolia for Ruby (#{::Algolia::VERSION})", "Jekyll (#{::Jekyll::VERSION})", "Ruby (#{RUBY_VERSION})" ].join('; ') ::Algolia.set_extra_header('User-Agent', user_agent) end
def self.update_records(index, records)
content will change its objectID as well.
should be updated but this case should never happen as changing a record
New records will be automatically added. Technically existing records
records - Array of records to update
index - Algolia Index to update
Public: Update records of the specified index
def self.update_records(index, records) batch_size = Configurator.algolia('indexing_batch_size') records.each_slice(batch_size) do |batch| Logger.log("I:Pushing #{batch.size} records") next if Configurator.dry_run? begin index.add_objects!(batch) rescue StandardError => error ErrorHandler.stop(error, records: records) end end end
def self.update_settings(index, settings)
settings - The hash of settings to pass to the index
index - The Algolia Index
Public: Update settings of the index
def self.update_settings(index, settings) Logger.verbose('I:Updating settings') return if Configurator.dry_run? begin index.set_settings(settings) rescue StandardError => error ErrorHandler.stop(error, settings: settings) end end