Remove SearchObserver, aim is to remove all observers

rails-observers gem is mostly unmaintained and is a pain to carry forward
new implementation contains significantly less magic as a bonus
This commit is contained in:
Sam 2016-12-22 13:13:14 +11:00
parent 31cda7b372
commit 0a78ae739d
16 changed files with 85 additions and 44 deletions

View File

@ -41,20 +41,26 @@ class Category < ActiveRecord::Base
validate :email_in_validator validate :email_in_validator
validate :ensure_slug validate :ensure_slug
after_create :create_category_definition
before_save :apply_permissions before_save :apply_permissions
before_save :downcase_email before_save :downcase_email
before_save :downcase_name before_save :downcase_name
after_create :create_category_definition
after_save :publish_category
after_destroy :publish_category_deletion
after_update :rename_category_definition, if: :name_changed?
after_create :delete_category_permalink
after_update :create_category_permalink, if: :slug_changed?
after_save :publish_discourse_stylesheet after_save :publish_discourse_stylesheet
after_save :publish_category
after_save :reset_topic_ids_cache
after_save :clear_url_cache
after_save :index_search
after_destroy :reset_topic_ids_cache
after_destroy :publish_category_deletion
after_create :delete_category_permalink
after_update :rename_category_definition, if: :name_changed?
after_update :create_category_permalink, if: :slug_changed?
has_one :category_search_data has_one :category_search_data
belongs_to :parent_category, class_name: 'Category' belongs_to :parent_category, class_name: 'Category'
@ -65,8 +71,6 @@ class Category < ActiveRecord::Base
has_many :category_tag_groups, dependent: :destroy has_many :category_tag_groups, dependent: :destroy
has_many :tag_groups, through: :category_tag_groups has_many :tag_groups, through: :category_tag_groups
after_save :reset_topic_ids_cache
after_destroy :reset_topic_ids_cache
scope :latest, -> { order('topic_count DESC') } scope :latest, -> { order('topic_count DESC') }
@ -425,9 +429,7 @@ SQL
@@url_cache = DistributedCache.new('category_url') @@url_cache = DistributedCache.new('category_url')
after_save do def clear_url_cache
# parent takes part in url calculation
# any change could invalidate multiples
@@url_cache.clear @@url_cache.clear
end end
@ -491,6 +493,10 @@ SQL
DiscourseStylesheets.cache.clear DiscourseStylesheets.cache.clear
end end
def index_search
SearchIndexer.index(self)
end
def self.find_by_slug(category_slug, parent_category_slug=nil) def self.find_by_slug(category_slug, parent_category_slug=nil)
if parent_category_slug if parent_category_slug
parent_category_id = self.where(slug: parent_category_slug, parent_category_id: nil).pluck(:id).first parent_category_id = self.where(slug: parent_category_slug, parent_category_id: nil).pluck(:id).first

View File

@ -51,6 +51,8 @@ class Post < ActiveRecord::Base
validates_with ::Validators::PostValidator validates_with ::Validators::PostValidator
after_save :index_search
# We can pass several creating options to a post via attributes # We can pass several creating options to a post via attributes
attr_accessor :image_sizes, :quoted_post_numbers, :no_bump, :invalidate_oneboxes, :cooking_options, :skip_unique_check attr_accessor :image_sizes, :quoted_post_numbers, :no_bump, :invalidate_oneboxes, :cooking_options, :skip_unique_check
@ -636,6 +638,10 @@ class Post < ActiveRecord::Base
PostTiming.where(topic_id: topic_id, post_number: post_number, user_id: user.id).exists? PostTiming.where(topic_id: topic_id, post_number: post_number, user_id: user.id).exists?
end end
def index_search
SearchIndexer.index(self)
end
private private
def parse_quote_into_arguments(quote) def parse_quote_into_arguments(quote)

View File

@ -198,6 +198,8 @@ class Topic < ActiveRecord::Base
TagUser.auto_track(topic_id: id) TagUser.auto_track(topic_id: id)
self.tags_changed = false self.tags_changed = false
end end
SearchIndexer.index(self)
end end
def initialize_default_values def initialize_default_values

View File

@ -99,6 +99,7 @@ class User < ActiveRecord::Base
after_save :refresh_avatar after_save :refresh_avatar
after_save :badge_grant after_save :badge_grant
after_save :expire_old_email_tokens after_save :expire_old_email_tokens
after_save :index_search
before_destroy do before_destroy do
# These tables don't have primary keys, so destroying them with activerecord is tricky: # These tables don't have primary keys, so destroying them with activerecord is tricky:
@ -913,6 +914,10 @@ class User < ActiveRecord::Base
end end
end end
def index_search
SearchIndexer.index(self)
end
def clear_global_notice_if_needed def clear_global_notice_if_needed
if admin && SiteSetting.has_login_hint if admin && SiteSetting.has_login_hint
SiteSetting.has_login_hint = false SiteSetting.has_login_hint = false

View File

@ -1,7 +1,14 @@
require_dependency 'search' require_dependency 'search'
class SearchObserver < ActiveRecord::Observer class SearchIndexer
observe :topic, :post, :user, :category
def self.disable
@disabled = true
end
def self.enable
@disabled = false
end
def self.scrub_html_for_search(html) def self.scrub_html_for_search(html)
HtmlScrubber.scrub(html) HtmlScrubber.scrub(html)
@ -72,17 +79,19 @@ class SearchObserver < ActiveRecord::Observer
end end
def self.index(obj) def self.index(obj)
return if @disabled
if obj.class == Post && obj.cooked_changed? if obj.class == Post && obj.cooked_changed?
if obj.topic if obj.topic
category_name = obj.topic.category.name if obj.topic.category category_name = obj.topic.category.name if obj.topic.category
SearchObserver.update_posts_index(obj.id, obj.cooked, obj.topic.title, category_name) SearchIndexer.update_posts_index(obj.id, obj.cooked, obj.topic.title, category_name)
SearchObserver.update_topics_index(obj.topic_id, obj.topic.title, obj.cooked) if obj.is_first_post? SearchIndexer.update_topics_index(obj.topic_id, obj.topic.title, obj.cooked) if obj.is_first_post?
else else
Rails.logger.warn("Orphan post skipped in search_observer, topic_id: #{obj.topic_id} post_id: #{obj.id} raw: #{obj.raw}") Rails.logger.warn("Orphan post skipped in search_indexer, topic_id: #{obj.topic_id} post_id: #{obj.id} raw: #{obj.raw}")
end end
end end
if obj.class == User && (obj.username_changed? || obj.name_changed?) if obj.class == User && (obj.username_changed? || obj.name_changed?)
SearchObserver.update_users_index(obj.id, obj.username_lower || '', obj.name ? obj.name.downcase : '') SearchIndexer.update_users_index(obj.id, obj.username_lower || '', obj.name ? obj.name.downcase : '')
end end
if obj.class == Topic && obj.title_changed? if obj.class == Topic && obj.title_changed?
@ -90,21 +99,17 @@ class SearchObserver < ActiveRecord::Observer
post = obj.posts.find_by(post_number: 1) post = obj.posts.find_by(post_number: 1)
if post if post
category_name = obj.category.name if obj.category category_name = obj.category.name if obj.category
SearchObserver.update_posts_index(post.id, post.cooked, obj.title, category_name) SearchIndexer.update_posts_index(post.id, post.cooked, obj.title, category_name)
SearchObserver.update_topics_index(obj.id, obj.title, post.cooked) SearchIndexer.update_topics_index(obj.id, obj.title, post.cooked)
end end
end end
end end
if obj.class == Category && obj.name_changed? if obj.class == Category && obj.name_changed?
SearchObserver.update_categories_index(obj.id, obj.name) SearchIndexer.update_categories_index(obj.id, obj.name)
end end
end end
def after_save(object)
SearchObserver.index(object)
end
class HtmlScrubber < Nokogiri::XML::SAX::Document class HtmlScrubber < Nokogiri::XML::SAX::Document
attr_reader :scrubbed attr_reader :scrubbed

View File

@ -85,7 +85,6 @@ module Discourse
:user_email_observer, :user_email_observer,
:user_action_observer, :user_action_observer,
:post_alert_observer, :post_alert_observer,
:search_observer
] ]
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
@ -169,6 +168,22 @@ module Discourse
end end
config.after_initialize do config.after_initialize do
# require common dependencies that are often required by plugins
# in the past observers would load them as side-effects
# correct behavior is for plugins to require stuff they need,
# however it would be a risky and breaking change not to require here
require_dependency 'category'
require_dependency 'post'
require_dependency 'topic'
require_dependency 'user'
require_dependency 'post_action'
require_dependency 'post_revision'
require_dependency 'notification'
require_dependency 'topic_user'
require_dependency 'group'
require_dependency 'user_field'
require_dependency 'post_action_type'
# So open id logs somewhere sane # So open id logs somewhere sane
OpenID::Util.logger = Rails.logger OpenID::Util.logger = Rails.logger
if plugins = Discourse.plugins if plugins = Discourse.plugins

View File

@ -54,7 +54,7 @@ class Search
posts.each do |post| posts.each do |post|
# force indexing # force indexing
post.cooked += " " post.cooked += " "
SearchObserver.index(post) SearchIndexer.index(post)
end end
posts = Post.joins(:topic) posts = Post.joins(:topic)
@ -67,7 +67,7 @@ class Search
posts.each do |post| posts.each do |post|
# force indexing # force indexing
post.cooked += " " post.cooked += " "
SearchObserver.index(post) SearchIndexer.index(post)
end end
nil nil

View File

@ -49,7 +49,7 @@ class Search
def self.blurb_for(cooked, term=nil, blurb_length=200) def self.blurb_for(cooked, term=nil, blurb_length=200)
cooked = SearchObserver::HtmlScrubber.scrub(cooked).squish cooked = SearchIndexer::HtmlScrubber.scrub(cooked).squish
blurb = nil blurb = nil
if term if term

View File

@ -18,8 +18,8 @@ def reindex_search(db=RailsMultisite::ConnectionManagement.current_db)
post_number = p["post_number"].to_i post_number = p["post_number"].to_i
topic_id = p["topic_id"].to_i topic_id = p["topic_id"].to_i
SearchObserver.update_posts_index(post_id, cooked, title, category) SearchIndexer.update_posts_index(post_id, cooked, title, category)
SearchObserver.update_topics_index(topic_id, title , cooked) if post_number == 1 SearchIndexer.update_topics_index(topic_id, title , cooked) if post_number == 1
putc "." putc "."
end end
@ -30,7 +30,7 @@ def reindex_search(db=RailsMultisite::ConnectionManagement.current_db)
id = u["id"] id = u["id"]
name = u["name"] name = u["name"]
username = u["username"] username = u["username"]
SearchObserver.update_users_index(id, username, name) SearchIndexer.update_users_index(id, username, name)
putc "." putc "."
end end
@ -41,7 +41,7 @@ def reindex_search(db=RailsMultisite::ConnectionManagement.current_db)
Category.exec_sql("select id, name from categories").each do |c| Category.exec_sql("select id, name from categories").each do |c|
id = c["id"] id = c["id"]
name = c["name"] name = c["name"]
SearchObserver.update_categories_index(id, name) SearchIndexer.update_categories_index(id, name)
end end
puts puts

View File

@ -10,7 +10,7 @@ describe Search do
end end
before do before do
ActiveRecord::Base.observers.enable :search_observer SearchIndexer.enable
end end
context 'post indexing observer' do context 'post indexing observer' do

View File

@ -5,7 +5,7 @@ describe SearchController do
context "integration" do context "integration" do
before do before do
ActiveRecord::Base.observers.enable :search_observer SearchIndexer.enable
end end
it "can search correctly" do it "can search correctly" do

View File

@ -1331,7 +1331,7 @@ describe UsersController do
let(:user) { Fabricate :user, username: "joecabot", name: "Lawrence Tierney" } let(:user) { Fabricate :user, username: "joecabot", name: "Lawrence Tierney" }
before do before do
ActiveRecord::Base.observers.enable :all SearchIndexer.enable
Fabricate :post, user: user, topic: topic Fabricate :post, user: user, topic: topic
end end

View File

@ -303,7 +303,7 @@ describe Topic do
context 'with a similar topic' do context 'with a similar topic' do
let!(:topic) { let!(:topic) {
ActiveRecord::Base.observers.enable :search_observer SearchIndexer.enable
post = create_post(title: "Evil trout is the dude who posted this topic") post = create_post(title: "Evil trout is the dude who posted this topic")
post.topic post.topic
} }

View File

@ -18,7 +18,7 @@ describe UserSearch do
let(:staged) { Fabricate :staged } let(:staged) { Fabricate :staged }
before do before do
ActiveRecord::Base.observers.enable :all SearchIndexer.enable
Fabricate :post, user: user1, topic: topic Fabricate :post, user: user1, topic: topic
Fabricate :post, user: user2, topic: topic2 Fabricate :post, user: user2, topic: topic2

View File

@ -107,6 +107,8 @@ Spork.prefork do
# disable all observers, enable as needed during specs # disable all observers, enable as needed during specs
# #
ActiveRecord::Base.observers.disable :all ActiveRecord::Base.observers.disable :all
SearchIndexer.disable
SiteSetting.provider.all.each do |setting| SiteSetting.provider.all.each do |setting|
SiteSetting.remove_override!(setting.name) SiteSetting.remove_override!(setting.name)
end end

View File

@ -1,13 +1,13 @@
require 'rails_helper' require 'rails_helper'
describe SearchObserver do describe SearchIndexer do
it 'correctly indexes chinese' do it 'correctly indexes chinese' do
SiteSetting.default_locale = 'zh_CN' SiteSetting.default_locale = 'zh_CN'
data = "你好世界" data = "你好世界"
expect(data.split(" ").length).to eq(1) expect(data.split(" ").length).to eq(1)
SearchObserver.update_posts_index(99, "你好世界", "", nil) SearchIndexer.update_posts_index(99, "你好世界", "", nil)
raw_data = PostSearchData.where(post_id: 99).pluck(:raw_data)[0] raw_data = PostSearchData.where(post_id: 99).pluck(:raw_data)[0]
expect(raw_data.split(' ').length).to eq(2) expect(raw_data.split(' ').length).to eq(2)
@ -16,13 +16,13 @@ describe SearchObserver do
it 'correctly indexes a post' do it 'correctly indexes a post' do
data = "<a>This</a> is a test" data = "<a>This</a> is a test"
SearchObserver.update_posts_index(99, data, "", nil) SearchIndexer.update_posts_index(99, data, "", nil)
raw_data, locale = PostSearchData.where(post_id: 99).pluck(:raw_data, :locale)[0] raw_data, locale = PostSearchData.where(post_id: 99).pluck(:raw_data, :locale)[0]
expect(raw_data).to eq("This is a test") expect(raw_data).to eq("This is a test")
expect(locale).to eq("en") expect(locale).to eq("en")
SearchObserver.update_posts_index(99, "tester", "", nil) SearchIndexer.update_posts_index(99, "tester", "", nil)
raw_data = PostSearchData.where(post_id: 99).pluck(:raw_data)[0] raw_data = PostSearchData.where(post_id: 99).pluck(:raw_data)[0]
expect(raw_data).to eq("tester") expect(raw_data).to eq("tester")