Skip to content

Commit

Permalink
🥗✨ Journal: Keywords can be merged into one another
Browse files Browse the repository at this point in the history
- https://github.com/zinc-collective/convene/issues/1713

There's a number of things that can and will go wrong with this code
eventually, including (but not limited to)

1. What happens when a `Keyword` has 1000s of entries?!
2. If this happens on-request-thread it will probably be very very slow;
   but if we do it *off* request-thread; the `Keyword#show` UI will be
   out-of-date; and we haven't implemented `ActionCable` yet

But hey, at least I will have a Heroku-CLI affordance for merging some of the
`Keywords` that are synonyms...

I'll probably want to build out a WUI as well; but I wanted to get the
basic logic written down and tested before I tilted at the WUI.
  • Loading branch information
zspencer committed Jul 28, 2023
1 parent 96738d1 commit a07c8f9
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 0 deletions.
6 changes: 6 additions & 0 deletions app/furniture/journal/keyword.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ class Keyword < ApplicationRecord
validates :canonical_keyword, presence: true, uniqueness: {case_sensitive: false, scope: :journal_id}
belongs_to :journal, inverse_of: :keywords

attribute :aliases, default: []

scope(:by_length, -> { order("LENGTH(canonical_keyword) DESC") })

scope(:search, lambda do |*keywords|
where("lower(aliases::text)::text[] && ARRAY[?]::text[]", keywords.map(&:downcase))
.or(where("lower(canonical_keyword) IN (?)", keywords.map(&:downcase)))
end)

def merge(other)
Merge.call(into: self, from: other)
end

def entries
journal.entries.matching_keywords(canonical_with_aliases)
end
Expand Down
18 changes: 18 additions & 0 deletions app/furniture/journal/keyword/merge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Journal
# Merges one {Journal::Keyword} into another and tidies up after.
class Keyword::Merge
# @param from {Journal::Keyword}
# @param into {Journal::Keyword}
def self.call(from:, into:)
into.aliases = into.aliases + from.canonical_with_aliases
Keyword.transaction do
into.save!
from.destroy!
into.entries.find_each do |entry|
entry.extract_keywords
entry.save!
end
end
end
end
end
18 changes: 18 additions & 0 deletions spec/furniture/journal/keyword_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@
it { is_expected.to validate_uniqueness_of(:canonical_keyword).case_insensitive.scoped_to(:journal_id) }
it { is_expected.to belong_to(:journal).inverse_of(:keywords) }

describe "#merge" do
it "adds aliases for the other `Keywords` canonical and aliases, deletes the other Keyword, and re-detects the entries keywords" do
entry = create(:journal_entry, body: "#GoodTime and #GoodTimes")

good_time_keyword = entry.journal.keywords.find_by(canonical_keyword: "GoodTime")
good_time_keyword.update!(aliases: ["GooodTime"])
good_times_keyword = entry.journal.keywords.find_by(canonical_keyword: "GoodTimes")

good_times_keyword.merge(good_time_keyword)
entry.reload

expect(good_times_keyword.aliases).to eq(["GoodTime", "GooodTime"])
expect(entry.keywords).not_to(include(good_time_keyword))
expect(entry.keywords).to(include(good_times_keyword))
expect(good_time_keyword).to be_destroyed
end
end

describe ".by_length" do
it "orders the results by the length of their canonical keyword" do
journal = create(:journal)
Expand Down

0 comments on commit a07c8f9

Please sign in to comment.