diff --git a/app/models/concerns/searchable.rb b/app/models/concerns/searchable.rb index 2f7efb1b4..903aaa4df 100644 --- a/app/models/concerns/searchable.rb +++ b/app/models/concerns/searchable.rb @@ -22,4 +22,8 @@ module Searchable def elasticsearch_doc self.class::SEARCH_CLASS.find_document(search_id) end + + def sync_related_elasticsearch_docs + self.class::DATA_SYNC_CLASS.new(self, saved_changes).sync_documents + end end diff --git a/app/models/podcast_episode.rb b/app/models/podcast_episode.rb index ea6867c63..e4248a01c 100644 --- a/app/models/podcast_episode.rb +++ b/app/models/podcast_episode.rb @@ -43,6 +43,9 @@ class PodcastEpisode < ApplicationRecord scope :reachable, -> { where(reachable: true) } scope :published, -> { joins(:podcast).where(podcasts: { published: true }) } scope :available, -> { reachable.published } + scope :for_user, lambda { |user| + joins(:podcast).where(podcasts: { creator_id: user.id }) + } algoliasearch per_environment: true do attribute :id diff --git a/app/models/user.rb b/app/models/user.rb index 53901ccf7..5872b9072 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -36,6 +36,7 @@ class User < ApplicationRecord SEARCH_SERIALIZER = Search::UserSerializer SEARCH_CLASS = Search::User + DATA_SYNC_CLASS = DataSync::Elasticsearch::User acts_as_followable acts_as_follower @@ -199,6 +200,7 @@ class User < ApplicationRecord after_create_commit :send_welcome_notification, :estimate_default_language after_commit :index_to_elasticsearch, on: %i[create update] + after_commit :sync_related_elasticsearch_docs, on: %i[create update] after_commit :remove_from_elasticsearch, on: [:destroy] algoliasearch per_environment: true, enqueue: :trigger_delayed_index do diff --git a/app/services/data_sync/elasticsearch/user.rb b/app/services/data_sync/elasticsearch/user.rb new file mode 100644 index 000000000..b97da726c --- /dev/null +++ b/app/services/data_sync/elasticsearch/user.rb @@ -0,0 +1,51 @@ +module DataSync + module Elasticsearch + class User + RELATED_DOCS = %i[ + articles + reactions + podcast_episodes + chat_channel_memberships + comments + ].freeze + + SHARED_USER_FIELDS = %i[ + username + name + pro + profile_image_url + ].freeze + + attr_accessor :user, :updated_fields + + delegate :articles, :chat_channel_memberships, :comments, to: :@user + + def initialize(user, updated_fields) + @user = user + @updated_fields = updated_fields.deep_symbolize_keys + end + + def sync_documents + return unless sync_needed? + + RELATED_DOCS.each do |relation_name| + send(relation_name).find_each(&:index_to_elasticsearch) + end + end + + private + + def sync_needed? + updated_fields.slice(*SHARED_USER_FIELDS).any? + end + + def reactions + user.reactions.readinglist + end + + def podcast_episodes + PodcastEpisode.for_user(user) + end + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 16f203f31..f00590aec 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -105,6 +105,12 @@ RSpec.describe User, type: :model do end end + it "on update syncs elasticsearch data" do + allow(user).to receive(:sync_related_elasticsearch_docs) + user.save + expect(user).to have_received(:sync_related_elasticsearch_docs) + end + it "on destroy enqueues job to delete user from elasticsearch" do user.save sidekiq_assert_enqueued_with(job: Search::RemoveFromElasticsearchIndexWorker, args: [described_class::SEARCH_CLASS.to_s, user.id]) do diff --git a/spec/services/data_sync/elasticsearch/user_spec.rb b/spec/services/data_sync/elasticsearch/user_spec.rb new file mode 100644 index 000000000..5b95d7388 --- /dev/null +++ b/spec/services/data_sync/elasticsearch/user_spec.rb @@ -0,0 +1,27 @@ +require "rails_helper" + +RSpec.describe DataSync::Elasticsearch::User, type: :service do + let!(:user) { create(:user) } + + describe "#sync_documents" do + it "reindexes RELATED_DOCS when sync is needed " do + syncer = described_class.new(user, username: %w[name1 name2]) + described_class::RELATED_DOCS.each do |method_name| + allow(syncer).to receive(method_name).and_call_original + end + syncer.sync_documents + described_class::RELATED_DOCS.each do |method_name| + expect(syncer).to have_received(method_name) + end + end + + it "does not reindex when sync is not needed" do + syncer = described_class.new(user, stackoverflow_url: [nil, "url"]) + described_class::RELATED_DOCS.each { |method_name| allow(syncer).to receive(method_name) } + syncer.sync_documents + described_class::RELATED_DOCS.each do |method_name| + expect(syncer).not_to have_received(method_name) + end + end + end +end