diff --git a/app/queries/homepage/articles_query.rb b/app/queries/homepage/articles_query.rb index ba5a2512e..98d1a81e7 100644 --- a/app/queries/homepage/articles_query.rb +++ b/app/queries/homepage/articles_query.rb @@ -69,10 +69,11 @@ module Homepage # would put all the previous filters in `OR` mode with tags, but what we need # is to only consider tags as a `OR` sub-condition if tags.present? - # `~` is the regexp operator and the `\M` modifier signifies the "end of word" + # `~` is the regexp operator, the `\m` modifier signifies the "beginning of word", + # and the `\M` modifier signifies the "end of word". # see https://www.postgresql.org/docs/11/functions-matching.html#FUNCTIONS-POSIX-REGEXP # for additional info - conditions = tags.map { |tag| relation.sanitize_sql_array(["cached_tag_list ~ ?", "#{tag}\\M"]) } + conditions = tags.map { |tag| relation.sanitize_sql_array(["cached_tag_list ~ ?", "\\m#{tag}\\M"]) } @relation = @relation.where(conditions.join(" OR ")) end diff --git a/app/services/search/postgres/reading_list.rb b/app/services/search/postgres/reading_list.rb index 407962ee3..d9fe6abf0 100644 --- a/app/services/search/postgres/reading_list.rb +++ b/app/services/search/postgres/reading_list.rb @@ -94,7 +94,7 @@ module Search # The preferred solution, as we don't need the `Tag` model itself, is to use # `articles.cached_tag_list` and the `~` regexp operator with it tags.each do |tag| - relation = relation.where("articles.cached_tag_list ~ ?", "#{tag}\\M") + relation = relation.where("articles.cached_tag_list ~ ?", "\\m#{tag}\\M") end # here we issue a COUNT(*) after all the conditions are applied, diff --git a/spec/queries/homepage/articles_query_spec.rb b/spec/queries/homepage/articles_query_spec.rb index a8fec5efc..1c9643e2c 100644 --- a/spec/queries/homepage/articles_query_spec.rb +++ b/spec/queries/homepage/articles_query_spec.rb @@ -127,11 +127,13 @@ RSpec.describe Homepage::ArticlesQuery, type: :query do expect(described_class.call(tags: %i[beginners ruby]).ids).to match_array([article1.id, article2.id]) end - it "does not return results for partial matches" do + it "does not return results for partial matches", :aggregate_failures do article1.tag_list.add(:javascript) article1.save expect(described_class.call(tags: %i[java]).ids).to be_empty + expect(described_class.call(tags: %i[asc]).ids).to be_empty + expect(described_class.call(tags: %i[script]).ids).to be_empty end end diff --git a/spec/services/search/postgres/reading_list_spec.rb b/spec/services/search/postgres/reading_list_spec.rb index fb619c8e4..df6b3e973 100644 --- a/spec/services/search/postgres/reading_list_spec.rb +++ b/spec/services/search/postgres/reading_list_spec.rb @@ -167,7 +167,7 @@ RSpec.describe Search::Postgres::ReadingList, type: :service do expect(extract_from_results(result, :path)).not_to include(article_2.path) end - it "does not match on partial tags" do + it "does not match on partial tags", :aggregate_failures do article_1.tag_list.add(:javascript) article_1.save! @@ -175,6 +175,12 @@ RSpec.describe Search::Postgres::ReadingList, type: :service do result = described_class.search_documents(user, tags: [:java]) expect(extract_from_results(result, :path)).to be_empty + + result = described_class.search_documents(user, tags: [:asc]) + expect(extract_from_results(result, :path)).to be_empty + + result = described_class.search_documents(user, tags: [:script]) + expect(extract_from_results(result, :path)).to be_empty end end