2017-01-31 17:13:45 +00:00
|
|
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
2017-05-02 15:21:13 +00:00
|
|
|
module HasSearchIndexBackend
|
2017-01-31 17:13:45 +00:00
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
|
|
|
included do
|
|
|
|
after_create :search_index_update
|
|
|
|
after_update :search_index_update
|
2020-03-02 09:33:37 +00:00
|
|
|
after_touch :search_index_update_touch
|
2017-01-31 17:13:45 +00:00
|
|
|
after_destroy :search_index_destroy
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
update search index, if configured - will be executed automatically
|
|
|
|
|
|
|
|
model = Model.find(123)
|
|
|
|
model.search_index_update
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_update
|
2017-09-11 00:50:05 +00:00
|
|
|
return true if ignore_search_indexing?(:update)
|
2017-01-31 17:13:45 +00:00
|
|
|
|
|
|
|
# start background job to transfer data to search index
|
2017-09-11 00:50:05 +00:00
|
|
|
return true if !SearchIndexBackend.enabled?
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2020-03-02 09:33:37 +00:00
|
|
|
SearchIndexAssociationsJob.perform_later(self.class.to_s, id)
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
update search index, if configured - will be executed automatically
|
|
|
|
|
|
|
|
model = Model.find(123)
|
|
|
|
model.search_index_update_touch
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_update_touch
|
|
|
|
return true if ignore_search_indexing?(:update)
|
|
|
|
|
|
|
|
# start background job to transfer data to search index
|
|
|
|
return true if !SearchIndexBackend.enabled?
|
|
|
|
|
2019-01-15 12:32:14 +00:00
|
|
|
SearchIndexJob.perform_later(self.class.to_s, id)
|
2017-06-16 22:53:20 +00:00
|
|
|
true
|
2017-01-31 17:13:45 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
2020-02-18 14:49:52 +00:00
|
|
|
update search index, if configured - will be executed automatically
|
|
|
|
|
|
|
|
model = Organizations.find(123)
|
|
|
|
result = model.search_index_update_associations_full
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
# Updates asscociation data for users and tickets of the organization in this example
|
|
|
|
result = true
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_update_associations_full
|
2021-01-27 12:55:22 +00:00
|
|
|
update_class = {
|
|
|
|
'Organization' => :organization_id,
|
|
|
|
'Group' => :group_id,
|
|
|
|
'Ticket::State' => :state_id,
|
|
|
|
'Ticket::Priority' => :priority_id,
|
|
|
|
}
|
|
|
|
update_column = update_class[self.class.to_s]
|
|
|
|
return if update_column.blank?
|
|
|
|
|
|
|
|
# reindex all object related tickets for the given object id
|
2020-02-18 14:49:52 +00:00
|
|
|
# we can not use the delta function for this because of the excluded
|
|
|
|
# ticket article attachments. see explain in delta function
|
2021-01-27 12:55:22 +00:00
|
|
|
Ticket.select('id').where(update_column => id).order(id: :desc).limit(10_000).pluck(:id).each do |ticket_id|
|
2020-03-02 09:33:37 +00:00
|
|
|
SearchIndexJob.perform_later('Ticket', ticket_id)
|
2020-02-18 14:49:52 +00:00
|
|
|
end
|
2021-01-27 12:55:22 +00:00
|
|
|
|
|
|
|
true
|
2020-02-18 14:49:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
update search index, if configured - will be executed automatically
|
|
|
|
|
|
|
|
model = Organizations.find(123)
|
|
|
|
result = model.search_index_update_associations_delta
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
# Updates asscociation data for users and tickets of the organization in this example
|
|
|
|
result = true
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_update_associations_delta
|
|
|
|
|
|
|
|
# start background job to transfer data to search index
|
|
|
|
return true if !SearchIndexBackend.enabled?
|
2020-03-02 14:25:33 +00:00
|
|
|
|
2021-01-27 09:58:35 +00:00
|
|
|
new_search_index_value = search_index_attribute_lookup(include_references: false)
|
2020-03-02 14:25:33 +00:00
|
|
|
return if new_search_index_value.blank?
|
2020-02-18 14:49:52 +00:00
|
|
|
|
|
|
|
Models.indexable.each do |local_object|
|
|
|
|
next if local_object == self.class
|
|
|
|
|
|
|
|
# delta update of associations is only possible for
|
|
|
|
# objects which are not containing modifications of the source
|
|
|
|
# https://github.com/zammad/zammad/blob/264853dcbe4e53addaf0f8e6df3735ceddc9de63/lib/tasks/search_index_es.rake#L266
|
|
|
|
# because of the exlusion of the article attachments for the ticket
|
|
|
|
# we dont have the attachment data available in the json store of the object.
|
|
|
|
# so the search index would lose the attachment information on the _update_by_query function
|
|
|
|
# https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html
|
|
|
|
next if local_object.to_s == 'Ticket'
|
|
|
|
|
|
|
|
local_object.new.attributes.each do |key, _value|
|
2020-10-06 13:13:50 +00:00
|
|
|
attribute_name = key.to_s
|
2020-02-18 14:49:52 +00:00
|
|
|
next if attribute_name.blank?
|
2020-10-06 13:13:50 +00:00
|
|
|
|
|
|
|
attribute_ref_name = local_object.search_index_attribute_ref_name(attribute_name)
|
2020-02-18 14:49:52 +00:00
|
|
|
next if attribute_ref_name.blank?
|
2020-10-06 13:13:50 +00:00
|
|
|
|
|
|
|
association = local_object.reflect_on_association(attribute_ref_name)
|
|
|
|
next if association.blank?
|
|
|
|
next if association.options[:polymorphic]
|
|
|
|
|
|
|
|
attribute_class = association.klass
|
2020-02-18 14:49:52 +00:00
|
|
|
next if attribute_class.blank?
|
|
|
|
next if attribute_class != self.class
|
|
|
|
|
|
|
|
data = {
|
2020-03-02 14:25:33 +00:00
|
|
|
attribute_ref_name => new_search_index_value,
|
2020-02-18 14:49:52 +00:00
|
|
|
}
|
|
|
|
where = {
|
|
|
|
attribute_name => id
|
|
|
|
}
|
|
|
|
SearchIndexBackend.update_by_query(local_object.to_s, data, where)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
2017-01-31 17:13:45 +00:00
|
|
|
delete search index object, will be executed automatically
|
|
|
|
|
|
|
|
model = Model.find(123)
|
|
|
|
model.search_index_destroy
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_destroy
|
2017-09-11 00:50:05 +00:00
|
|
|
return true if ignore_search_indexing?(:destroy)
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-01-31 17:13:45 +00:00
|
|
|
SearchIndexBackend.remove(self.class.to_s, id)
|
2017-06-16 22:53:20 +00:00
|
|
|
true
|
2017-01-31 17:13:45 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
collect data to index and send to backend
|
|
|
|
|
|
|
|
ticket = Ticket.find(123)
|
|
|
|
result = ticket.search_index_update_backend
|
|
|
|
|
|
|
|
returns
|
|
|
|
|
|
|
|
result = true # false
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_update_backend
|
|
|
|
# fill up with search data
|
|
|
|
attributes = search_index_attribute_lookup
|
2017-09-11 00:50:05 +00:00
|
|
|
return true if !attributes
|
2017-01-31 17:13:45 +00:00
|
|
|
|
|
|
|
# update backend
|
|
|
|
SearchIndexBackend.add(self.class.to_s, attributes)
|
2017-06-16 22:53:20 +00:00
|
|
|
true
|
2017-01-31 17:13:45 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def ignore_search_indexing?(_action)
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
# methods defined here are going to extend the class, not the instance of it
|
2021-06-01 08:44:48 +00:00
|
|
|
class_methods do # rubocop:disable Metrics/BlockLength
|
2017-01-31 17:13:45 +00:00
|
|
|
|
|
|
|
=begin
|
|
|
|
|
2019-07-31 08:23:48 +00:00
|
|
|
serve method to ignore model attributes in search index
|
2017-01-31 17:13:45 +00:00
|
|
|
|
|
|
|
class Model < ApplicationModel
|
2017-05-02 15:21:13 +00:00
|
|
|
include HasSearchIndexBackend
|
2017-01-31 17:13:45 +00:00
|
|
|
search_index_attributes_ignored :password, :image
|
|
|
|
end
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_attributes_ignored(*attributes)
|
|
|
|
@search_index_attributes_ignored = attributes
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
reload search index with full data
|
|
|
|
|
|
|
|
Model.search_index_reload
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def search_index_reload
|
2019-09-02 15:32:54 +00:00
|
|
|
tolerance = 10
|
2017-01-31 17:13:45 +00:00
|
|
|
tolerance_count = 0
|
2021-06-01 08:44:48 +00:00
|
|
|
batch_size = 100
|
|
|
|
query = all.order(created_at: :desc)
|
|
|
|
total = query.count
|
|
|
|
query.find_in_batches(batch_size: batch_size).with_index do |group, batch|
|
|
|
|
group.each do |item|
|
|
|
|
next if item.ignore_search_indexing?(:destroy)
|
|
|
|
|
|
|
|
begin
|
|
|
|
item.search_index_update_backend
|
|
|
|
rescue => e
|
|
|
|
logger.error "Unable to send #{item.class}.find(#{item.id}).search_index_update_backend backend: #{e.inspect}"
|
|
|
|
tolerance_count += 1
|
|
|
|
sleep 15
|
|
|
|
raise "Unable to send #{item.class}.find(#{item.id}).search_index_update_backend backend: #{e.inspect}" if tolerance_count == tolerance
|
|
|
|
end
|
2017-01-31 17:13:45 +00:00
|
|
|
end
|
2021-06-01 08:44:48 +00:00
|
|
|
puts "\t#{[(batch + 1) * batch_size, total].min}/#{total}" # rubocop:disable Rails/Output
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-01-31 17:13:45 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|