Init version of ES support. Thanks to Roy!
This commit is contained in:
parent
3a3e06b07d
commit
f04a12696c
7 changed files with 365 additions and 20 deletions
|
@ -4,6 +4,7 @@ class ApplicationModel < ActiveRecord::Base
|
||||||
include ApplicationModel::Assets
|
include ApplicationModel::Assets
|
||||||
include ApplicationModel::HistoryLogBase
|
include ApplicationModel::HistoryLogBase
|
||||||
include ApplicationModel::ActivityStreamBase
|
include ApplicationModel::ActivityStreamBase
|
||||||
|
include ApplicationModel::SearchIndexBase
|
||||||
|
|
||||||
self.abstract_class = true
|
self.abstract_class = true
|
||||||
|
|
||||||
|
@ -23,9 +24,13 @@ class ApplicationModel < ActiveRecord::Base
|
||||||
after_update :history_update
|
after_update :history_update
|
||||||
after_destroy :history_destroy
|
after_destroy :history_destroy
|
||||||
|
|
||||||
|
after_create :search_index_update
|
||||||
|
after_update :search_index_update
|
||||||
|
after_destroy :search_index_destroy
|
||||||
|
|
||||||
# create instance accessor
|
# create instance accessor
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :activity_stream_support_config, :history_support_config
|
attr_accessor :activity_stream_support_config, :history_support_config, :search_index_support_config
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :history_changes_last_done
|
attr_accessor :history_changes_last_done
|
||||||
|
@ -453,28 +458,65 @@ class OwnModel < ApplicationModel
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
check string/varchar size and cut them if needed
|
serve methode to configure and enable search index support for this model
|
||||||
|
|
||||||
|
class Model < ApplicationModel
|
||||||
|
search_index_support :ignore_attributes => {
|
||||||
|
:create_article_type_id => true,
|
||||||
|
:create_article_sender_id => true,
|
||||||
|
:article_count => true,
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def check_limits
|
def self.search_index_support(data = {})
|
||||||
self.attributes.each {|attribute|
|
@search_index_support_config = data
|
||||||
next if !self[ attribute[0] ]
|
|
||||||
next if self[ attribute[0] ].class != String
|
|
||||||
next if self[ attribute[0] ].empty?
|
|
||||||
column = self.class.columns_hash[ attribute[0] ]
|
|
||||||
limit = column.limit
|
|
||||||
if column && limit
|
|
||||||
current_length = attribute[1].to_s.length
|
|
||||||
if limit < current_length
|
|
||||||
puts "WARNING: cut string because of database length #{self.class.to_s}.#{attribute[0]}(#{limit} but is #{current_length}:#{attribute[1].to_s})"
|
|
||||||
self[attribute[0]] = attribute[1][ 0, limit ]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
update search index, if configured - will be executed automatically
|
||||||
|
|
||||||
|
model = Model.find(123)
|
||||||
|
model.search_index_update
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def search_index_update
|
||||||
|
return if !self.class.search_index_support_config
|
||||||
|
search_index_update_backend
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
delete search index object, will be executed automatically
|
||||||
|
|
||||||
|
model = Model.find(123)
|
||||||
|
model.search_index_destroy
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def search_index_destroy
|
||||||
|
return if !self.class.search_index_support_config
|
||||||
|
SearchIndexBackend.remove( self.class.to_s, self.id )
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
reload search index with full data
|
||||||
|
|
||||||
|
Model.search_index_reload
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.search_index_reload
|
||||||
|
return if !@search_index_support_config
|
||||||
|
self.all.each { |item|
|
||||||
|
item.search_index_update_backend
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -557,7 +599,7 @@ delete object activity stream, will be executed automatically
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def activity_stream_destroy
|
def activity_stream_destroy
|
||||||
return if !@activity_stream_support_config
|
return if !self.class.activity_stream_support_config
|
||||||
ActivityStream.remove( self.class.to_s, self.id )
|
ActivityStream.remove( self.class.to_s, self.id )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -703,10 +745,35 @@ delete object history, will be executed automatically
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def history_destroy
|
def history_destroy
|
||||||
return if !@history_support_config
|
return if !self.class.history_support_config
|
||||||
History.remove( self.class.to_s, self.id )
|
History.remove( self.class.to_s, self.id )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
check string/varchar size and cut them if needed
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def check_limits
|
||||||
|
self.attributes.each {|attribute|
|
||||||
|
next if !self[ attribute[0] ]
|
||||||
|
next if self[ attribute[0] ].class != String
|
||||||
|
next if self[ attribute[0] ].empty?
|
||||||
|
column = self.class.columns_hash[ attribute[0] ]
|
||||||
|
limit = column.limit
|
||||||
|
if column && limit
|
||||||
|
current_length = attribute[1].to_s.length
|
||||||
|
if limit < current_length
|
||||||
|
puts "WARNING: cut string because of database length #{self.class.to_s}.#{attribute[0]}(#{limit} but is #{current_length}:#{attribute[1].to_s})"
|
||||||
|
self[attribute[0]] = attribute[1][ 0, limit ]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
destory object dependencies, will be executed automatically
|
destory object dependencies, will be executed automatically
|
||||||
|
|
109
app/models/application_model/search_index_base.rb
Normal file
109
app/models/application_model/search_index_base.rb
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
module ApplicationModel::SearchIndexBase
|
||||||
|
|
||||||
|
=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
|
||||||
|
return if !self.class.search_index_support_config
|
||||||
|
|
||||||
|
# default ignored attributes
|
||||||
|
ignore_attributes = {
|
||||||
|
:created_at => true,
|
||||||
|
:updated_at => true,
|
||||||
|
:created_by_id => true,
|
||||||
|
:updated_by_id => true,
|
||||||
|
:active => true,
|
||||||
|
}
|
||||||
|
if self.class.search_index_support_config[:ignore_attributes]
|
||||||
|
self.class.search_index_support_config[:ignore_attributes].each {|key, value|
|
||||||
|
ignore_attributes[key] = value
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# remove ignored attributes
|
||||||
|
attributes = self.attributes
|
||||||
|
ignore_attributes.each {|key, value|
|
||||||
|
next if value != true
|
||||||
|
attributes.delete( key.to_s )
|
||||||
|
}
|
||||||
|
|
||||||
|
# fill up with search data
|
||||||
|
attributes = search_index_attribute_lookup(attributes, self)
|
||||||
|
return if !attributes
|
||||||
|
|
||||||
|
# update backend
|
||||||
|
if self.class.column_names.include? 'active'
|
||||||
|
if self.active
|
||||||
|
SearchIndexBackend.add( self.class.to_s, attributes )
|
||||||
|
else
|
||||||
|
SearchIndexBackend.remove( self.class.to_s, self.id )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
SearchIndexBackend.add( self.class.to_s, attributes )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
lookup name of ref. objects
|
||||||
|
|
||||||
|
attributes = search_index_attribute_lookup(attributes, Ticket)
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
attributes # object with lookup data
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def search_index_attribute_lookup(attributes, ref_object)
|
||||||
|
attributes_new = {}
|
||||||
|
attributes.each {|key, value|
|
||||||
|
next if !value
|
||||||
|
|
||||||
|
# get attribute name
|
||||||
|
attribute_name = key.to_s
|
||||||
|
next if attribute_name[-3,3] != '_id'
|
||||||
|
attribute_name = attribute_name[ 0, attribute_name.length-3 ]
|
||||||
|
|
||||||
|
# check if attribute method exists
|
||||||
|
next if !ref_object.respond_to?( attribute_name )
|
||||||
|
|
||||||
|
# check if method has own class
|
||||||
|
relation_class = ref_object.send(attribute_name).class
|
||||||
|
next if !relation_class
|
||||||
|
|
||||||
|
# lookup ref object
|
||||||
|
relation_model = relation_class.lookup( :id => value )
|
||||||
|
next if !relation_model
|
||||||
|
|
||||||
|
# get name of ref object
|
||||||
|
value = nil
|
||||||
|
if relation_model['name']
|
||||||
|
value = relation_model['name']
|
||||||
|
elsif relation_model.respond_to?('fullname')
|
||||||
|
value = relation_model.send('fullname')
|
||||||
|
end
|
||||||
|
next if !value
|
||||||
|
|
||||||
|
# save name of ref object
|
||||||
|
attributes_new[ attribute_name ] = value
|
||||||
|
attributes.delete(key)
|
||||||
|
}
|
||||||
|
attributes_new.merge(attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -9,5 +9,6 @@ class Organization < ApplicationModel
|
||||||
|
|
||||||
activity_stream_support :role => 'Admin'
|
activity_stream_support :role => 'Admin'
|
||||||
history_support
|
history_support
|
||||||
|
search_index_support
|
||||||
|
|
||||||
end
|
end
|
|
@ -10,6 +10,8 @@ class Ticket < ApplicationModel
|
||||||
include Ticket::HistoryLog
|
include Ticket::HistoryLog
|
||||||
require 'ticket/activity_stream_log'
|
require 'ticket/activity_stream_log'
|
||||||
include Ticket::ActivityStreamLog
|
include Ticket::ActivityStreamLog
|
||||||
|
require 'ticket/search_index'
|
||||||
|
include Ticket::SearchIndex
|
||||||
extend Ticket::Search
|
extend Ticket::Search
|
||||||
|
|
||||||
before_create :check_generate, :check_defaults
|
before_create :check_generate, :check_defaults
|
||||||
|
@ -31,6 +33,12 @@ class Ticket < ApplicationModel
|
||||||
:article_count => true,
|
:article_count => true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
search_index_support :ignore_attributes => {
|
||||||
|
:create_article_type_id => true,
|
||||||
|
:create_article_sender_id => true,
|
||||||
|
:article_count => true,
|
||||||
|
}
|
||||||
|
|
||||||
belongs_to :group
|
belongs_to :group
|
||||||
has_many :articles, :class_name => 'Ticket::Article', :after_add => :cache_update, :after_remove => :cache_update
|
has_many :articles, :class_name => 'Ticket::Article', :after_add => :cache_update, :after_remove => :cache_update
|
||||||
belongs_to :organization
|
belongs_to :organization
|
||||||
|
|
67
app/models/ticket/search_index.rb
Normal file
67
app/models/ticket/search_index.rb
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
module Ticket::SearchIndex
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
log activity for this object
|
||||||
|
|
||||||
|
ticket = Ticket.find(123)
|
||||||
|
result = ticket.search_index_update_backend
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
result = true # false
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def search_index_update_backend
|
||||||
|
return if !self.class.search_index_support_config
|
||||||
|
|
||||||
|
# default ignored attributes
|
||||||
|
ignore_attributes = {
|
||||||
|
:created_at => true,
|
||||||
|
:updated_at => true,
|
||||||
|
:created_by_id => true,
|
||||||
|
:updated_by_id => true,
|
||||||
|
:active => true,
|
||||||
|
}
|
||||||
|
if self.class.search_index_support_config[:ignore_attributes]
|
||||||
|
self.class.search_index_support_config[:ignore_attributes].each {|key, value|
|
||||||
|
ignore_attributes[key] = value
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
attributes = self.attributes
|
||||||
|
ignore_attributes.each {|key, value|
|
||||||
|
next if value != true
|
||||||
|
attributes.delete( key.to_s )
|
||||||
|
}
|
||||||
|
attributes = search_index_attribute_lookup( attributes, self )
|
||||||
|
|
||||||
|
# add article data
|
||||||
|
articles = Ticket::Article.where( :ticket_id => self.id )
|
||||||
|
attributes['articles_all'] = []
|
||||||
|
attributes['articles_external'] = []
|
||||||
|
articles.each {|article|
|
||||||
|
article_attributes = article.attributes
|
||||||
|
article_attributes.delete('created_by_id')
|
||||||
|
article_attributes.delete('updated_by_id')
|
||||||
|
article_attributes.delete('updated_at')
|
||||||
|
article_attributes.delete('references')
|
||||||
|
article_attributes.delete('message_id_md5')
|
||||||
|
article_attributes.delete('message_id')
|
||||||
|
article_attributes.delete('in_reply_to')
|
||||||
|
article_attributes.delete('ticket_id')
|
||||||
|
article_attributes = search_index_attribute_lookup( article_attributes, article )
|
||||||
|
attributes['articles_all'].push article_attributes
|
||||||
|
if !article.internal
|
||||||
|
attributes['articles_external'].push article_attributes
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
return if !attributes
|
||||||
|
SearchIndexBackend.add(self.class.to_s, attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -36,6 +36,17 @@ class User < ApplicationModel
|
||||||
:image_source => true,
|
:image_source => true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
search_index_support(
|
||||||
|
:ignore_attributes => {
|
||||||
|
:password => true,
|
||||||
|
:image => true,
|
||||||
|
:image_source => true,
|
||||||
|
:source => true,
|
||||||
|
:login_failed => true,
|
||||||
|
:preferences => true,
|
||||||
|
:locale => true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
|
82
lib/search_index_backend.rb
Normal file
82
lib/search_index_backend.rb
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class SearchIndexBackend
|
||||||
|
@@index = "zammad_#{Rails.env}"
|
||||||
|
@@url = 'http://127.0.0.1:9000'
|
||||||
|
@@user = 'elasticsearch'
|
||||||
|
@@pw = 'zammad'
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
add new object to search index
|
||||||
|
|
||||||
|
SearchIndexBackend.add( 'Ticket', some_data_object )
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.add(type, data)
|
||||||
|
|
||||||
|
url = "#{@@url}/#{@@index}/#{type}/#{data[:id]}"
|
||||||
|
|
||||||
|
puts "# curl -X POST \"#{url}\" -d '#{data.to_json}'"
|
||||||
|
|
||||||
|
conn = Faraday.new( :url => url )
|
||||||
|
if @@user && @@pw
|
||||||
|
conn.basic_auth( @@user, @@pw )
|
||||||
|
end
|
||||||
|
|
||||||
|
response = conn.post do |req|
|
||||||
|
req.url url
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.body = data.to_json
|
||||||
|
end
|
||||||
|
# puts response.body.to_s
|
||||||
|
puts "# #{response.status.to_s}"
|
||||||
|
return true if response.success?
|
||||||
|
data = JSON.parse( response.body )
|
||||||
|
return { :data => data, :response => response }
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
remove whole data from index
|
||||||
|
|
||||||
|
SearchIndexBackend.remove( 'Ticket', 123 )
|
||||||
|
|
||||||
|
SearchIndexBackend.remove( 'Ticket' )
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.remove( type, o_id = nil )
|
||||||
|
if o_id
|
||||||
|
url = "#{@@url}/#{@@index}/#{type}/#{o_id}"
|
||||||
|
else
|
||||||
|
url = "#{@@url}/#{@@index}/#{type}"
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "# curl -X DELETE \"#{url}\""
|
||||||
|
|
||||||
|
conn = Faraday.new( :url => url )
|
||||||
|
if @@user && @@pw
|
||||||
|
conn.basic_auth( @@user, @@pw )
|
||||||
|
end
|
||||||
|
response = conn.delete url
|
||||||
|
# puts response.body.to_s
|
||||||
|
puts "# #{response.status.to_s}"
|
||||||
|
return true if response.success?
|
||||||
|
data = JSON.parse( response.body )
|
||||||
|
return { :data => data, :response => response }
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
return all activity entries of an user
|
||||||
|
|
||||||
|
result = SearchIndexBackend.search( user )
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.search(user,limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue