From 91972dd2c4e2a243095adea21069aa0b0d9c0033 Mon Sep 17 00:00:00 2001 From: Molly Struve Date: Tue, 14 Apr 2020 08:38:09 -0500 Subject: [PATCH] ensure that when Tag and ActsAsTaggableOn::Tag are updated their respective related docs are as well (#7218) --- app/lib/acts_as_taggable_on/tag.rb | 6 +++- app/models/tag.rb | 2 ++ app/services/data_sync/elasticsearch/tag.rb | 31 +++++++++++++++++++ spec/lib/acts_as_taggable_on/tag_spec.rb | 22 +++++++++++++ spec/models/tag_spec.rb | 22 +++++++++++++ .../data_sync/elasticsearch/tag_spec.rb | 8 +++++ 6 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 app/services/data_sync/elasticsearch/tag.rb create mode 100644 spec/services/data_sync/elasticsearch/tag_spec.rb diff --git a/app/lib/acts_as_taggable_on/tag.rb b/app/lib/acts_as_taggable_on/tag.rb index f1db22c44..715cb1eee 100644 --- a/app/lib/acts_as_taggable_on/tag.rb +++ b/app/lib/acts_as_taggable_on/tag.rb @@ -1,7 +1,11 @@ module ActsAsTaggableOn class Tag - after_commit on: :create do + after_commit on: %i[create update] do ::Tag.find(id).index_to_elasticsearch end + + after_commit on: :update do + DataSync::Elasticsearch::Tag.new(self).call + end end end diff --git a/app/models/tag.rb b/app/models/tag.rb index 62d43c24b..83c10a67e 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -33,11 +33,13 @@ class Tag < ActsAsTaggableOn::Tag after_commit :bust_cache after_commit :index_to_elasticsearch, on: %i[create update] + after_commit :sync_related_elasticsearch_docs, on: [:update] after_commit :remove_from_elasticsearch, on: [:destroy] include Searchable SEARCH_SERIALIZER = Search::TagSerializer SEARCH_CLASS = Search::Tag + DATA_SYNC_CLASS = DataSync::Elasticsearch::Tag # This model doesn't inherit from ApplicationRecord so this has to be included include Purgeable diff --git a/app/services/data_sync/elasticsearch/tag.rb b/app/services/data_sync/elasticsearch/tag.rb new file mode 100644 index 000000000..8f1ae3bd1 --- /dev/null +++ b/app/services/data_sync/elasticsearch/tag.rb @@ -0,0 +1,31 @@ +module DataSync + module Elasticsearch + class Tag < Base + RELATED_DOCS = %i[ + articles + podcast_episodes + reactions + ].freeze + + SHARED_FIELDS = %i[ + keywords_for_search + ].freeze + + private + + def articles + ::Article.cached_tagged_with(updated_record.name) + end + + def reactions + ::Reaction.readinglist.where(reactable: articles) + end + + def podcast_episodes + ::PodcastEpisode.where( + id: updated_record.taggings.where(taggable_type: "PodcastEpisode").select(:taggable_id), + ) + end + end + end +end diff --git a/spec/lib/acts_as_taggable_on/tag_spec.rb b/spec/lib/acts_as_taggable_on/tag_spec.rb index 50711fb43..6e6cfdd1b 100644 --- a/spec/lib/acts_as_taggable_on/tag_spec.rb +++ b/spec/lib/acts_as_taggable_on/tag_spec.rb @@ -9,5 +9,27 @@ RSpec.describe ActsAsTaggableOn::Tag, type: :lib do tag = Tag.find_by(name: tag_name) expect(tag.elasticsearch_doc).not_to be_nil end + + it "syncs related elasticsearch documents" do + article = create(:article) + podcast_episode = create(:podcast_episode) + tag = article.tags.first + podcast_episode.tags << tag + reaction = create(:reaction, reactable: article, category: "readinglist") + new_keywords = "keyword1, keyword2, keyword3" + sidekiq_perform_enqueued_jobs + + tag.update(keywords_for_search: new_keywords) + sidekiq_perform_enqueued_jobs + expect(collect_keywords(article)).to include(new_keywords) + expect( + reaction.elasticsearch_doc.dig("_source", "reactable", "tags").flat_map { |t| t["keywords_for_search"] }, + ).to include(new_keywords) + expect(collect_keywords(podcast_episode)).to include(new_keywords) + end + end + + def collect_keywords(record) + record.elasticsearch_doc.dig("_source", "tags").flat_map { |t| t["keywords_for_search"] } end end diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index 823883683..45e353285 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -103,5 +103,27 @@ RSpec.describe Tag, type: :model do tag.destroy end end + + it "syncs related elasticsearch documents" do + article = create(:article) + podcast_episode = create(:podcast_episode) + tag = described_class.find(article.tags.first.id) + podcast_episode.tags << tag + reaction = create(:reaction, reactable: article, category: "readinglist") + new_keywords = "keyword1, keyword2, keyword3" + sidekiq_perform_enqueued_jobs + + tag.update(keywords_for_search: new_keywords) + sidekiq_perform_enqueued_jobs + expect(collect_keywords(article)).to include(new_keywords) + expect( + reaction.elasticsearch_doc.dig("_source", "reactable", "tags").flat_map { |t| t["keywords_for_search"] }, + ).to include(new_keywords) + expect(collect_keywords(podcast_episode)).to include(new_keywords) + end + end + + def collect_keywords(record) + record.elasticsearch_doc.dig("_source", "tags").flat_map { |t| t["keywords_for_search"] } end end diff --git a/spec/services/data_sync/elasticsearch/tag_spec.rb b/spec/services/data_sync/elasticsearch/tag_spec.rb new file mode 100644 index 000000000..589f39743 --- /dev/null +++ b/spec/services/data_sync/elasticsearch/tag_spec.rb @@ -0,0 +1,8 @@ +require "rails_helper" + +RSpec.describe DataSync::Elasticsearch::Tag, type: :service do + it "defines necessary constants" do + expect(described_class::RELATED_DOCS).not_to be_nil + expect(described_class::SHARED_FIELDS).not_to be_nil + end +end