diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 1cc0619dd..9ea92fffa 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -39,10 +39,14 @@ class ApplicationRecord < ActiveRecord::Base where.not("#{qualified_column_for(attr)} ~ ?", "(?e)" + value) end - def where_array_includes(attr, values) + def where_array_includes_any(attr, values) where("#{qualified_column_for(attr)} && ARRAY[?]", values) end + def where_array_includes_all(attr, values) + where("#{qualified_column_for(attr)} @> ARRAY[?]", values) + end + def where_array_count(attr, value) relation = all qualified_column = "cardinality(#{qualified_column_for(attr)})" @@ -100,6 +104,10 @@ class ApplicationRecord < ActiveRecord::Base column = column_for_attribute(name) type = column.type || reflect_on_association(name)&.class_name + if column.array? + return search_array_attribute(name, type, params) + end + case type when "User" search_user_attribute(name, params) @@ -110,11 +118,7 @@ class ApplicationRecord < ActiveRecord::Base when :boolean search_boolean_attribute(name, params) when :integer, :datetime - if column.array? - search_array_attribute(name, type, params) - else - numeric_attribute_matches(name, params[name]) - end + numeric_attribute_matches(name, params[name]) else raise NotImplementedError, "unhandled attribute type" end @@ -173,9 +177,16 @@ class ApplicationRecord < ActiveRecord::Base def search_array_attribute(name, type, params) relation = all - if params[:"#{name}_include"] && type == :integer - items = params[:"#{name}_include"].to_s.scan(/\d+/).map(&:to_i) - relation = relation.where_array_includes(name, items) + if params[:"#{name}_include_any"] + items = params[:"#{name}_include_any"].to_s.scan(/[^[:space:]]+/) + items = items.map(&:to_i) if type == :integer + + relation = relation.where_array_includes_any(name, items) + elsif params[:"#{name}_include_all"] + items = params[:"#{name}_include_any"].to_s.scan(/[^[:space:]]+/) + items = items.map(&:to_i) if type == :integer + + relation = relation.where_array_includes_all(name, items) end if params[:"#{name.to_s.singularize}_count"] diff --git a/app/models/pool_archive.rb b/app/models/pool_archive.rb index 9994de4c7..a75e1eea6 100644 --- a/app/models/pool_archive.rb +++ b/app/models/pool_archive.rb @@ -19,7 +19,7 @@ class PoolArchive < ApplicationRecord end def for_post_id(post_id) - where_array_includes(:added_post_ids, [post_id]).or(where_array_includes(:removed_post_ids, [post_id])) + where_array_includes_any(:added_post_ids, [post_id]).or(where_array_includes_any(:removed_post_ids, [post_id])) end def search(params) diff --git a/app/models/post_archive.rb b/app/models/post_archive.rb index 877eef489..12e3b006c 100644 --- a/app/models/post_archive.rb +++ b/app/models/post_archive.rb @@ -18,9 +18,23 @@ class PostArchive < ApplicationRecord end module SearchMethods + def changed_tags_include(tag) + where_array_includes_all(:added_tags, [tag]).or(where_array_includes_all(:removed_tags, [tag])) + end + + def changed_tags_include_all(tags) + tags.reduce(all) do |relation, tag| + relation.changed_tags_include(tag) + end + end + def search(params) q = super - q = q.search_attributes(params, :updater_id, :post_id, :rating, :rating_changed, :parent_id, :parent_changed, :source, :source_changed, :version) + q = q.search_attributes(params, :updater_id, :post_id, :tags, :added_tags, :removed_tags, :rating, :rating_changed, :parent_id, :parent_changed, :source, :source_changed, :version) + + if params[:changed_tags] + q = q.changed_tags_include_all(params[:changed_tags].scan(/[^[:space:]]+/)) + end if params[:updater_name].present? q = q.where(updater_id: User.name_to_id(params[:updater_name])) diff --git a/app/views/post_versions/search.html.erb b/app/views/post_versions/search.html.erb index a4ba56d0a..2bb25e21f 100644 --- a/app/views/post_versions/search.html.erb +++ b/app/views/post_versions/search.html.erb @@ -5,6 +5,9 @@ <%= search_form_for(post_versions_path) do |f| %> <%= f.input :updated_at, label: "Date" %> <%= f.input :updater_name, label: "Updater", input_html: { "data-autocomplete": "user" } %> + <%= f.input :changed_tags_include_all, label: "Changed tags" %> + <%= f.input :added_tags_include_all, label: "Added tags" %> + <%= f.input :removed_tags_include_all, label: "Removed tags" %> <%= f.input :post_id %> <%= f.input :parent_id %> <%= f.input :rating %> diff --git a/app/views/posts/partials/index/_related.html.erb b/app/views/posts/partials/index/_related.html.erb index 13a9021eb..939781864 100644 --- a/app/views/posts/partials/index/_related.html.erb +++ b/app/views/posts/partials/index/_related.html.erb @@ -13,9 +13,10 @@ <% end %>
  • <%= link_to "Deleted", posts_path(:tags => "#{params[:tags]} status:deleted"), :rel => "nofollow" %>
  • -
  • <%= link_to "Random", random_posts_path(:tags => params[:tags]), :id => "random-post", :rel => "nofollow", :"data-shortcut" => "r" %>
  • - + <% if Tag.is_simple_tag?(params[:tags]) %> +
  • <%= link_to "History", post_versions_path(search: { changed_tags: params[:tags] }) %>
  • + <% end %>
  • <%= link_to "Count", posts_counts_path(:tags => params[:tags]) %>
  • diff --git a/test/functional/post_versions_controller_test.rb b/test/functional/post_versions_controller_test.rb index 682fb1bda..d3bb931ac 100644 --- a/test/functional/post_versions_controller_test.rb +++ b/test/functional/post_versions_controller_test.rb @@ -7,7 +7,7 @@ class PostVersionsControllerTest < ActionDispatch::IntegrationTest context "The post versions controller" do context "index action" do - setup do + setup do @user.as_current do @post = create(:post) travel(2.hours) do @@ -34,6 +34,17 @@ class PostVersionsControllerTest < ActionDispatch::IntegrationTest assert_response :success assert_select "#post-version-#{@post2.versions[0].id}", false end + + should "list all versions for search[changed_tags]" do + get post_versions_path, as: :json, params: { search: { changed_tags: "1" }} + assert_response :success + assert_equal @versions[1].id, response.parsed_body[1]["id"].to_i + assert_equal @versions[2].id, response.parsed_body[0]["id"].to_i + + get post_versions_path, as: :json, params: { search: { changed_tags: "1 2" }} + assert_response :success + assert_equal @versions[1].id, response.parsed_body[0]["id"].to_i + end end end end diff --git a/test/unit/pool_test.rb b/test/unit/pool_test.rb index 75d24199d..aef8ab2b1 100644 --- a/test/unit/pool_test.rb +++ b/test/unit/pool_test.rb @@ -66,8 +66,8 @@ class PoolTest < ActiveSupport::TestCase @post1 = create(:post, tag_string: "pool:pool1") @post2 = create(:post, tag_string: "pool:pool2") - assert_equal([@pool1.id], Pool.search(post_ids_include: @post1.id).pluck(:id)) - assert_equal([@pool2.id, @pool1.id], Pool.search(post_ids_include: "#{@post1.id} #{@post2.id}").pluck(:id)) + assert_equal([@pool1.id], Pool.search(post_ids_include_any: @post1.id).pluck(:id)) + assert_equal([@pool2.id, @pool1.id], Pool.search(post_ids_include_any: "#{@post1.id} #{@post2.id}").pluck(:id)) end should "find pools by post id count" do