From 3e8e33e663b627819f16be497f9482f27eff3515 Mon Sep 17 00:00:00 2001 From: evazion Date: Thu, 7 Apr 2022 02:28:57 -0500 Subject: [PATCH] post queries: fix handling of '~' operator. Fix queries like `(~a ~b) (~c ~d)` being handled like `~a ~b ~c ~d`. Caused by trimming AND nodes from the tree before rewriting the '~' operator, which caused `~a` terms to be incorrectly lifted out of subexpressions. --- app/logical/post_query.rb | 7 +++++- test/unit/post_query_builder_test.rb | 34 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/app/logical/post_query.rb b/app/logical/post_query.rb index dce0f5b18..9e2607c2a 100644 --- a/app/logical/post_query.rb +++ b/app/logical/post_query.rb @@ -13,7 +13,7 @@ class PostQuery # Return a new PostQuery with aliases replaced. def self.normalize(...) - PostQuery.new(...).replace_aliases.trim + PostQuery.new(...).replace_aliases.rewrite_opts.trim end def initialize(search_or_ast, current_user: User.anonymous, tag_limit: nil, safe_mode: false, hide_deleted_posts: false) @@ -129,6 +129,11 @@ class PostQuery build(ast.trim) end + # Return a new PostQuery with the '~' operator replaced with OR clauses. + def rewrite_opts + build(ast.rewrite_opts) + end + # Return a new PostQuery with aliases replaced. def replace_aliases return self if aliases.empty? diff --git a/test/unit/post_query_builder_test.rb b/test/unit/post_query_builder_test.rb index 0fd9afee1..addd09ae5 100644 --- a/test/unit/post_query_builder_test.rb +++ b/test/unit/post_query_builder_test.rb @@ -1311,6 +1311,40 @@ class PostQueryBuilderTest < ActiveSupport::TestCase assert_tag_match([], "-aaa id:>0") assert_tag_match([], "-a* rating:s") end + + should "succeed for nested OR clauses" do + post1 = create(:post, tag_string: "a c") + post2 = create(:post, tag_string: "b d") + post3 = create(:post, tag_string: "a") + post4 = create(:post, tag_string: "d") + + assert_tag_match([post3, post2, post1], "~a ~b") + assert_tag_match([post3, post2, post1], "a or b") + + assert_tag_match([post4, post2, post1], "~c ~d") + assert_tag_match([post4, post2, post1], "c or d") + + assert_tag_match([post2, post1], "(a or b) (c or d)") + assert_tag_match([post2, post1], "(~a ~b) (~c ~d)") + + assert_tag_match([post2, post1], "a c or b d") + assert_tag_match([post2, post1], "(a c) or (b d)") + assert_tag_match([post2, post1], "~(a c) or ~(b d)") + end + + should "succeed for metatags combined with OR clauses" do + post1 = create(:post, rating: "s") + post2 = create(:post, rating: "q") + post3 = create(:post, rating: "e") + + assert_tag_match([post2, post1], "~rating:s ~rating:q") + assert_tag_match([post3, post2, post1], "~rating:s ~rating:q ~rating:e") + + assert_tag_match([post2, post1], "rating:s or rating:q") + assert_tag_match([post3, post2, post1], "rating:s or rating:q or rating:e") + + assert_tag_match([post2, post1], "id:#{post1.id} or rating:q") + end end context "Parsing:" do