2014-02-03 12:08:41 +00:00
|
|
|
$LOAD_PATH << './lib'
|
|
|
|
require 'rubygems'
|
|
|
|
|
|
|
|
namespace :searchindex do
|
2020-11-10 07:47:16 +00:00
|
|
|
task :drop, [:opts] => %i[environment searchindex:configured searchindex:version_supported] do |_t, _args|
|
2019-06-20 10:45:27 +00:00
|
|
|
print 'drop indexes...'
|
2014-02-03 12:08:41 +00:00
|
|
|
|
|
|
|
# drop indexes
|
2019-06-20 10:45:27 +00:00
|
|
|
if es_multi_index?
|
|
|
|
Models.indexable.each do |local_object|
|
|
|
|
SearchIndexBackend.index(
|
|
|
|
action: 'delete',
|
|
|
|
name: local_object.name,
|
|
|
|
)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
SearchIndexBackend.index(
|
|
|
|
action: 'delete',
|
|
|
|
)
|
|
|
|
end
|
2017-12-15 13:58:41 +00:00
|
|
|
puts 'done'
|
2014-02-03 12:08:41 +00:00
|
|
|
|
2017-12-15 13:58:41 +00:00
|
|
|
Rake::Task['searchindex:drop_pipeline'].execute
|
2014-02-03 12:08:41 +00:00
|
|
|
end
|
|
|
|
|
2020-11-10 07:47:16 +00:00
|
|
|
task :create, [:opts] => %i[environment searchindex:configured searchindex:version_supported] do |_t, _args|
|
2017-12-15 13:58:41 +00:00
|
|
|
print 'create indexes...'
|
2014-02-03 12:08:41 +00:00
|
|
|
|
2019-06-20 10:45:27 +00:00
|
|
|
if es_multi_index?
|
|
|
|
Setting.set('es_multi_index', true)
|
|
|
|
else
|
|
|
|
Setting.set('es_multi_index', false)
|
2017-12-04 01:12:55 +00:00
|
|
|
end
|
2017-12-04 00:24:58 +00:00
|
|
|
|
2018-10-18 22:40:57 +00:00
|
|
|
settings = {
|
|
|
|
'index.mapping.total_fields.limit': 2000,
|
|
|
|
}
|
2017-12-04 00:24:58 +00:00
|
|
|
|
2018-07-18 14:00:06 +00:00
|
|
|
# create indexes
|
2019-06-20 10:45:27 +00:00
|
|
|
if es_multi_index?
|
|
|
|
Models.indexable.each do |local_object|
|
|
|
|
SearchIndexBackend.index(
|
|
|
|
action: 'create',
|
|
|
|
name: local_object.name,
|
|
|
|
data: {
|
|
|
|
mappings: get_mapping_properties_object(local_object),
|
|
|
|
settings: settings,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
mapping = {}
|
|
|
|
Models.indexable.each do |local_object|
|
|
|
|
mapping.merge!(get_mapping_properties_object(local_object))
|
|
|
|
end
|
|
|
|
SearchIndexBackend.index(
|
|
|
|
action: 'create',
|
|
|
|
data: {
|
|
|
|
mappings: mapping,
|
|
|
|
settings: settings,
|
|
|
|
}
|
|
|
|
)
|
2017-12-15 13:58:41 +00:00
|
|
|
end
|
|
|
|
|
2018-07-18 14:00:06 +00:00
|
|
|
puts 'done'
|
|
|
|
|
2017-12-15 13:58:41 +00:00
|
|
|
Rake::Task['searchindex:create_pipeline'].execute
|
|
|
|
end
|
|
|
|
|
2020-11-10 07:47:16 +00:00
|
|
|
task :create_pipeline, [:opts] => %i[environment searchindex:configured searchindex:version_supported] do |_t, _args|
|
2019-06-20 10:45:27 +00:00
|
|
|
if !es_pipeline?
|
|
|
|
Setting.set('es_pipeline', '')
|
|
|
|
next
|
2017-12-15 13:58:41 +00:00
|
|
|
end
|
2017-12-04 00:24:58 +00:00
|
|
|
|
2017-12-15 13:58:41 +00:00
|
|
|
# update processors
|
|
|
|
pipeline = Setting.get('es_pipeline')
|
|
|
|
if pipeline.blank?
|
|
|
|
pipeline = "zammad#{rand(999_999_999_999)}"
|
2017-12-04 00:24:58 +00:00
|
|
|
Setting.set('es_pipeline', pipeline)
|
2017-12-15 13:58:41 +00:00
|
|
|
end
|
2019-12-04 09:52:54 +00:00
|
|
|
|
|
|
|
# define pipeline_field_attributes
|
|
|
|
# ES 5.6 and nower has no ignore_missing support
|
|
|
|
pipeline_field_attributes = {
|
|
|
|
ignore_failure: true,
|
|
|
|
}
|
|
|
|
if es_multi_index?
|
|
|
|
pipeline_field_attributes = {
|
|
|
|
ignore_failure: true,
|
|
|
|
ignore_missing: true,
|
|
|
|
}
|
|
|
|
end
|
2017-12-15 13:58:41 +00:00
|
|
|
print 'create pipeline (pipeline)... '
|
|
|
|
SearchIndexBackend.processors(
|
|
|
|
"_ingest/pipeline/#{pipeline}": [
|
|
|
|
{
|
|
|
|
action: 'delete',
|
|
|
|
},
|
|
|
|
{
|
2018-12-19 17:31:51 +00:00
|
|
|
action: 'create',
|
2017-12-15 13:58:41 +00:00
|
|
|
description: 'Extract zammad-attachment information from arrays',
|
2018-12-19 17:31:51 +00:00
|
|
|
processors: [
|
2017-12-15 13:58:41 +00:00
|
|
|
{
|
|
|
|
foreach: {
|
2019-12-04 09:52:54 +00:00
|
|
|
field: 'article',
|
|
|
|
processor: {
|
2017-12-15 13:58:41 +00:00
|
|
|
foreach: {
|
2019-12-04 09:52:54 +00:00
|
|
|
field: '_ingest._value.attachment',
|
|
|
|
processor: {
|
2017-12-15 13:58:41 +00:00
|
|
|
attachment: {
|
2019-12-04 09:52:54 +00:00
|
|
|
target_field: '_ingest._value',
|
|
|
|
field: '_ingest._value._content',
|
|
|
|
}.merge(pipeline_field_attributes),
|
2017-12-04 00:24:58 +00:00
|
|
|
}
|
2019-12-04 09:52:54 +00:00
|
|
|
}.merge(pipeline_field_attributes),
|
2017-12-04 00:24:58 +00:00
|
|
|
}
|
2019-12-04 09:52:54 +00:00
|
|
|
}.merge(pipeline_field_attributes),
|
2020-02-19 16:41:25 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
foreach: {
|
|
|
|
field: 'attachment',
|
|
|
|
processor: {
|
|
|
|
attachment: {
|
|
|
|
target_field: '_ingest._value',
|
|
|
|
field: '_ingest._value._content',
|
|
|
|
}.merge(pipeline_field_attributes),
|
|
|
|
}
|
|
|
|
}.merge(pipeline_field_attributes),
|
2017-12-15 13:58:41 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
)
|
|
|
|
puts 'done'
|
|
|
|
end
|
|
|
|
|
2020-11-10 07:47:16 +00:00
|
|
|
task :drop_pipeline, [:opts] => %i[environment searchindex:configured searchindex:version_supported] do |_t, _args|
|
2019-06-20 10:45:27 +00:00
|
|
|
next if !es_pipeline?
|
2017-12-15 13:58:41 +00:00
|
|
|
|
|
|
|
# update processors
|
|
|
|
pipeline = Setting.get('es_pipeline')
|
|
|
|
next if pipeline.blank?
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-12-15 13:58:41 +00:00
|
|
|
print 'delete pipeline (pipeline)... '
|
|
|
|
SearchIndexBackend.processors(
|
|
|
|
"_ingest/pipeline/#{pipeline}": [
|
|
|
|
{
|
|
|
|
action: 'delete',
|
|
|
|
},
|
|
|
|
]
|
|
|
|
)
|
|
|
|
puts 'done'
|
2014-02-03 12:08:41 +00:00
|
|
|
end
|
|
|
|
|
2020-11-10 07:47:16 +00:00
|
|
|
task :reload, [:opts] => %i[environment searchindex:configured searchindex:version_supported] do |_t, _args|
|
2015-04-27 13:20:16 +00:00
|
|
|
puts 'reload data...'
|
2018-10-03 06:23:22 +00:00
|
|
|
Models.indexable.each do |model_class|
|
2015-08-16 09:30:31 +00:00
|
|
|
puts " reload #{model_class}"
|
2015-08-16 09:33:16 +00:00
|
|
|
started_at = Time.zone.now
|
|
|
|
puts " - started at #{started_at}"
|
2015-08-16 09:30:31 +00:00
|
|
|
model_class.search_index_reload
|
2015-08-16 09:33:16 +00:00
|
|
|
took = Time.zone.now - started_at
|
|
|
|
puts " - took #{took.to_i} seconds"
|
2017-10-01 12:31:29 +00:00
|
|
|
end
|
2014-02-03 12:08:41 +00:00
|
|
|
|
|
|
|
end
|
|
|
|
|
2020-11-10 07:47:16 +00:00
|
|
|
task :refresh, [:opts] => %i[environment searchindex:configured searchindex:version_supported] do |_t, _args|
|
2019-09-24 13:04:42 +00:00
|
|
|
print 'refresh all indexes...'
|
|
|
|
|
|
|
|
SearchIndexBackend.refresh
|
|
|
|
end
|
|
|
|
|
2020-11-10 07:47:16 +00:00
|
|
|
task :rebuild, [:opts] => %i[environment searchindex:configured searchindex:version_supported] do |_t, _args|
|
2015-04-27 13:20:16 +00:00
|
|
|
Rake::Task['searchindex:drop'].execute
|
|
|
|
Rake::Task['searchindex:create'].execute
|
|
|
|
Rake::Task['searchindex:reload'].execute
|
2014-02-03 12:08:41 +00:00
|
|
|
end
|
2020-02-18 14:49:52 +00:00
|
|
|
|
|
|
|
task :version_supported, [:opts] => :environment do |_t, _args|
|
|
|
|
next if es_version_supported?
|
|
|
|
|
2020-11-10 07:47:16 +00:00
|
|
|
abort "Your Elasticsearch version is not supported! Please update your version to a greater equal than 5.6.0 (Your current version: #{es_version})."
|
|
|
|
end
|
|
|
|
|
|
|
|
task :configured, [:opts] => :environment do |_t, _args|
|
|
|
|
next if es_configured?
|
|
|
|
|
|
|
|
abort "You have not configured Elasticsearch (Setting.get('es_url'))."
|
2020-02-18 14:49:52 +00:00
|
|
|
end
|
2014-02-03 12:08:41 +00:00
|
|
|
end
|
2018-07-18 14:00:06 +00:00
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
This function will return a index mapping based on the
|
|
|
|
attributes of the database table of the existing object.
|
|
|
|
|
|
|
|
mapping = get_mapping_properties_object(Ticket)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
mapping = {
|
|
|
|
User: {
|
|
|
|
properties: {
|
|
|
|
firstname: {
|
|
|
|
type: 'keyword',
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def get_mapping_properties_object(object)
|
2019-06-20 10:45:27 +00:00
|
|
|
|
|
|
|
name = object.name
|
|
|
|
if es_multi_index?
|
|
|
|
name = '_doc'
|
|
|
|
end
|
2018-07-18 14:00:06 +00:00
|
|
|
result = {
|
2019-06-20 10:45:27 +00:00
|
|
|
name => {
|
2018-07-18 14:00:06 +00:00
|
|
|
properties: {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
store_columns = %w[preferences data]
|
|
|
|
|
2019-06-20 10:45:27 +00:00
|
|
|
# for elasticsearch 6.x and later
|
|
|
|
string_type = 'text'
|
2019-10-30 07:45:31 +00:00
|
|
|
string_raw = { 'type': 'keyword', 'ignore_above': 5012 }
|
2019-06-20 10:45:27 +00:00
|
|
|
boolean_raw = { 'type': 'boolean' }
|
|
|
|
|
|
|
|
# for elasticsearch 5.6 and lower
|
|
|
|
if !es_multi_index?
|
|
|
|
string_type = 'string'
|
|
|
|
string_raw = { 'type': 'string', 'index': 'not_analyzed' }
|
|
|
|
boolean_raw = { 'type': 'boolean', 'index': 'not_analyzed' }
|
|
|
|
end
|
|
|
|
|
2018-07-18 14:00:06 +00:00
|
|
|
object.columns_hash.each do |key, value|
|
|
|
|
if value.type == :string && value.limit && value.limit <= 5000 && store_columns.exclude?(key)
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
|
|
|
type: string_type,
|
2018-07-18 14:00:06 +00:00
|
|
|
fields: {
|
2019-10-30 07:45:31 +00:00
|
|
|
keyword: string_raw,
|
2018-07-18 14:00:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
elsif value.type == :integer
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
type: 'integer',
|
|
|
|
}
|
|
|
|
elsif value.type == :datetime
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
type: 'date',
|
|
|
|
}
|
|
|
|
elsif value.type == :boolean
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
2018-12-19 17:31:51 +00:00
|
|
|
type: 'boolean',
|
2018-07-18 14:00:06 +00:00
|
|
|
fields: {
|
2019-10-30 07:45:31 +00:00
|
|
|
keyword: boolean_raw,
|
2018-07-18 14:00:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
elsif value.type == :binary
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
type: 'binary',
|
|
|
|
}
|
|
|
|
elsif value.type == :bigint
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
type: 'long',
|
|
|
|
}
|
|
|
|
elsif value.type == :decimal
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
type: 'float',
|
|
|
|
}
|
|
|
|
elsif value.type == :date
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][key] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
type: 'date',
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# es with mapper-attachments plugin
|
|
|
|
if object.name == 'Ticket'
|
|
|
|
|
2019-06-20 10:45:27 +00:00
|
|
|
# do not server attachments if document is requested
|
|
|
|
result[name][:_source] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
excludes: ['article.attachment']
|
|
|
|
}
|
|
|
|
|
2019-06-20 10:45:27 +00:00
|
|
|
# for elasticsearch 5.5 and lower
|
|
|
|
if !es_pipeline?
|
|
|
|
result[name][:_source] = {
|
2018-07-18 14:00:06 +00:00
|
|
|
excludes: ['article.attachment']
|
|
|
|
}
|
2019-06-20 10:45:27 +00:00
|
|
|
result[name][:properties][:article] = {
|
2018-12-19 17:31:51 +00:00
|
|
|
type: 'nested',
|
2018-07-18 14:00:06 +00:00
|
|
|
include_in_parent: true,
|
2018-12-19 17:31:51 +00:00
|
|
|
properties: {
|
2018-07-18 14:00:06 +00:00
|
|
|
attachment: {
|
|
|
|
type: 'attachment',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-19 16:41:25 +00:00
|
|
|
if object.name == 'KnowledgeBase::Answer::Translation'
|
|
|
|
# do not server attachments if document is requested
|
|
|
|
result[name][:_source] = {
|
|
|
|
excludes: ['attachment']
|
|
|
|
}
|
|
|
|
|
|
|
|
# for elasticsearch 5.5 and lower
|
|
|
|
if !es_pipeline?
|
|
|
|
result[name][:_source] = {
|
|
|
|
excludes: ['attachment']
|
|
|
|
}
|
|
|
|
result[name][:properties][:attachment] = {
|
|
|
|
type: 'attachment',
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-06-20 10:45:27 +00:00
|
|
|
return result if es_type_in_mapping?
|
|
|
|
|
|
|
|
result[name]
|
|
|
|
end
|
|
|
|
|
|
|
|
# get es version
|
|
|
|
def es_version
|
2019-09-16 09:15:12 +00:00
|
|
|
@es_version ||= begin
|
|
|
|
info = SearchIndexBackend.info
|
|
|
|
number = nil
|
|
|
|
if info.present?
|
|
|
|
number = info['version']['number'].to_s
|
|
|
|
end
|
|
|
|
number
|
2019-06-20 10:45:27 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-18 14:49:52 +00:00
|
|
|
def es_version_supported?
|
|
|
|
version_split = es_version.split('.')
|
2020-02-18 19:51:31 +00:00
|
|
|
version = "#{version_split[0]}#{format('%<minor>03d', minor: version_split[1])}#{format('%<patch>03d', patch: version_split[2])}".to_i
|
2020-02-18 14:49:52 +00:00
|
|
|
|
|
|
|
# only versions greater/equal than 5.6.0 are supported
|
|
|
|
return if version < 5_006_000
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2019-06-20 10:45:27 +00:00
|
|
|
# no es_pipeline for elasticsearch 5.5 and lower
|
|
|
|
def es_pipeline?
|
|
|
|
number = es_version
|
|
|
|
return false if number.blank?
|
2019-09-16 09:21:10 +00:00
|
|
|
return false if number.match?(/^[2-4]\./)
|
|
|
|
return false if number.match?(/^5\.[0-5]\./)
|
2019-06-20 10:45:27 +00:00
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2019-12-04 09:52:54 +00:00
|
|
|
# no multi index for elasticsearch 5.6 and lower
|
2019-06-20 10:45:27 +00:00
|
|
|
def es_multi_index?
|
|
|
|
number = es_version
|
|
|
|
return false if number.blank?
|
2019-09-16 09:21:10 +00:00
|
|
|
return false if number.match?(/^[2-5]\./)
|
2019-06-20 10:45:27 +00:00
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# no type in mapping
|
|
|
|
def es_type_in_mapping?
|
|
|
|
number = es_version
|
|
|
|
return true if number.blank?
|
2019-09-16 09:21:10 +00:00
|
|
|
return true if number.match?(/^[2-6]\./)
|
2019-06-20 10:45:27 +00:00
|
|
|
|
|
|
|
false
|
2018-07-18 14:00:06 +00:00
|
|
|
end
|
2020-11-10 07:47:16 +00:00
|
|
|
|
|
|
|
# is es configured?
|
|
|
|
def es_configured?
|
|
|
|
return false if Setting.get('es_url').blank?
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|