post queries: track whether metatag values are quoted.

This is necessary for the `commentary:` metatag, which has different
behavior depending on whether the metatag value is quoted. For example,
`commentary:translated` finds translated commentaries, while
`commentary:"translated"` finds commentaries containing the literal word
"translated".
This commit is contained in:
evazion
2022-04-06 17:15:02 -05:00
parent 2adc530ba0
commit a4d43ae72a
3 changed files with 17 additions and 12 deletions

View File

@@ -60,8 +60,8 @@ class PostQuery
AST.new(:tag, [name]) AST.new(:tag, [name])
end end
def metatag(name, value) def metatag(name, value, quoted = false)
AST.new(:metatag, [name, value]) AST.new(:metatag, [name, value, quoted])
end end
end end
@@ -211,7 +211,7 @@ class PostQuery
"none" "none"
in [:tag, name] in [:tag, name]
name name
in [:metatag, name, value] in [:metatag, name, value, quoted]
"#{name}:#{quoted_value}" "#{name}:#{quoted_value}"
in [:wildcard, name] in [:wildcard, name]
"(wildcard #{name})" "(wildcard #{name})"
@@ -231,7 +231,7 @@ class PostQuery
name name
in [:tag, name] in [:tag, name]
name name
in [:metatag, name, value] in [:metatag, name, value, quoted]
"#{name}:#{quoted_value}" "#{name}:#{quoted_value}"
in :not, child in :not, child
child.term? ? "-#{child.to_infix}" : "-(#{child.to_infix})" child.term? ? "-#{child.to_infix}" : "-(#{child.to_infix})"
@@ -354,11 +354,16 @@ class PostQuery
args.second if metatag? args.second if metatag?
end end
# @return [String, nil] True if the metatag's value was enclosed in quotes.
def quoted?
args.third if metatag?
end
# @return [String, nil] The value of the metatag as a quoted string, if a metatag node. # @return [String, nil] The value of the metatag as a quoted string, if a metatag node.
def quoted_value def quoted_value
return nil unless metatag? return nil unless metatag?
if value.include?(" ") || value.starts_with?('"') || value.starts_with?("'") || value.empty? if quoted?
%Q{"#{value.gsub(/"/, '\\"')}"} %Q{"#{value.gsub(/"/, '\\"')}"}
else else
value value

View File

@@ -164,8 +164,8 @@ class PostQuery
if accept(METATAG_NAME_REGEX) if accept(METATAG_NAME_REGEX)
name = @scanner.matched.delete_suffix(":").downcase name = @scanner.matched.delete_suffix(":").downcase
name = name.singularize + "_count" if name.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS) name = name.singularize + "_count" if name.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS)
value = quoted_string quoted, value = quoted_string
node(:metatag, name, value) node(:metatag, name, value, quoted)
end end
end end
@@ -173,13 +173,13 @@ class PostQuery
if accept('"') if accept('"')
a = accept(/([^"\\]|\\")*/).gsub(/\\"/, '"') # handle backslash escaped quotes a = accept(/([^"\\]|\\")*/).gsub(/\\"/, '"') # handle backslash escaped quotes
expect('"') expect('"')
a [true, a]
elsif accept("'") elsif accept("'")
a = accept(/([^'\\]|\\')*/).gsub(/\\'/, "'") # handle backslash escaped quotes a = accept(/([^'\\]|\\')*/).gsub(/\\'/, "'") # handle backslash escaped quotes
expect("'") expect("'")
a [true, a]
else else
string(/[^ ]+/) [false, string(/[^ ]+/)]
end end
end end

View File

@@ -69,14 +69,14 @@ class PostQueryParserTest < ActiveSupport::TestCase
assert_parse_equals('source:""', 'source:""') assert_parse_equals('source:""', 'source:""')
assert_parse_equals('source:"\""', 'source:"\""') assert_parse_equals('source:"\""', 'source:"\""')
assert_parse_equals(%q{source:"don't say \"lazy\" okay"}, %q{source:"don't say \"lazy\" okay"}) assert_parse_equals(%q{source:"don't say \"lazy\" okay"}, %q{source:"don't say \"lazy\" okay"})
assert_parse_equals(%q{(and source:foo)bar a)}, %q{(a (source:"foo)bar"))}) assert_parse_equals(%q{(and source:"foo)bar" a)}, %q{(a (source:"foo)bar"))})
assert_parse_equals('source:"foo bar"', "source:'foo bar'") assert_parse_equals('source:"foo bar"', "source:'foo bar'")
assert_parse_equals("source:foobar'(", "source:foobar'(") assert_parse_equals("source:foobar'(", "source:foobar'(")
assert_parse_equals('source:""', "source:''") assert_parse_equals('source:""', "source:''")
assert_parse_equals('source:"\'"', "source:'\\''") assert_parse_equals('source:"\'"', "source:'\\''")
assert_parse_equals(%q{source:"don't say \"lazy\" okay"}, %q{source:'don\'t say "lazy" okay'}) assert_parse_equals(%q{source:"don't say \"lazy\" okay"}, %q{source:'don\'t say "lazy" okay'})
assert_parse_equals(%q{(and source:foo)bar a)}, %q{(a (source:'foo)bar'))}) assert_parse_equals(%q{(and source:"foo)bar" a)}, %q{(a (source:'foo)bar'))})
end end
should "parse metatag synonyms correctly" do should "parse metatag synonyms correctly" do