Init version of ES support. Thanks to Roy!

This commit is contained in:
Martin Edenhofer 2014-01-27 23:59:41 +01:00
parent 3a3e06b07d
commit f04a12696c
7 changed files with 365 additions and 20 deletions

View file

@ -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] ] end
next if self[ attribute[0] ].class != String
next if self[ attribute[0] ].empty? =begin
column = self.class.columns_hash[ attribute[0] ]
limit = column.limit update search index, if configured - will be executed automatically
if column && limit
current_length = attribute[1].to_s.length model = Model.find(123)
if limit < current_length model.search_index_update
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 def search_index_update
return if !self.class.search_index_support_config
search_index_update_backend
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

View 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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View 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