在 Ruby 中将 SphinxSearch 查询语法转换为布尔搜索字符串
Convert SphinxSearch query syntax to boolean search string in Ruby
我一直在思考将以下 Sphinx 搜索查询转换为典型网络搜索或门户中常用的查询的最简单方法是什么,例如布尔搜索字符串,反之亦然
(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R
需要转换为
(A OR B) AND "C D" AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
为了示例目的也略有不同
(A | B) C D (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R
应该是
(A OR B) AND C AND D AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
为清楚起见,
“A”可以是任何单词和任何大小写,不区分大小写。除非在引号内,否则空格在起始语法中表示 AND。所以 AB 只是一个词,例如Java。 (A|B) 之间的 space 不重要 (A|B) 等同于 ( A | B ) 或 (A | B) 等。每个字母表示一个单词。
其中一些查询会很长 - 最多 500 个术语。虽然这不是一个巨大的处理开销,但我在想什么是转换它的最佳(最有效)方法。标记化、Regex/pattern 匹配、简单替换、递归等。你们会推荐什么?
读者可能正在寻找一个优雅的,至少不是 hackish 的解决方案来解决这个问题。那也是我的 objective,但是,唉,这是我能想到的最好的了。
代码
def convert(str)
subs = []
str.gsub(/"[^"]*"| *\| */) do |s|
if s.match?(/ *\| */)
'|'
else
subs << s
'*'
end
end.gsub(/ +/, ' AND ').
gsub(/[*|]/) { |s| s == '|' ? ' OR ' : subs.shift }
end
例子
puts convert(%Q{(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R})
#-> (A OR B) AND "C D" AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
puts convert(%Q{(A|B) C D (E| "F G" |"H I J") ("K L" ("M N" | "O P")) Q R})
#-> (A OR B) AND C AND D AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
请注意,在此示例中,在某些管道之后的 and/or 之前没有 space,并且在双引号字符串之外的某些地方有多个 space。
puts convert(%Q{(Ant | Bat) Cat Dog (Emu | "Frog Gorilla" | "Hen Ibex Jackel") ("Khawla Lynx" ("Magpie Newt" | "Ocelot Penguin")) Quail Rabbit})
#-> (Ant OR Bat) AND Cat AND Dog AND (Emu OR "Frog Gorilla" OR "Hen Ibex Jackel") AND ("Khawla Lynx" AND ("Magpie Newt" OR "Ocelot Penguin")) AND Quail AND Rabbit
这里我用单词替换了大写字母。
说明
要了解这是如何工作的,让
str = %Q{(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R}
#=> "(A | B) \"C D\" (E | \"F G\" | \"H I J\") (\"K L\" (\"M N\" | \"O P\")) Q R"
然后
subs = []
str.gsub(/"[^"]*"| *\| */) do |s|
if s.match?(/ *\| */)
'|'
else
subs << s
'*'
end
end
#=> "(A|B) * (E|*|*) (* (*|*)) Q R"
subs
#=> ["\"C D\"", "\"F G\"", "\"H I J\"", "\"K L\"", "\"M N\"", "\"O P\""]
如您所见,我删除了管道周围的 spaces 并将所有带引号的字符串替换为星号,将这些字符串保存在数组 subs
中,以便我以后可以将星号替换为他们的原始值。星号的选择当然是随意的。
正则表达式为“匹配零个或多个字符的双引号字符串或竖线 ('|'
) 可选地在 and/or 后跟 spaces”。
作为这些替换的结果,space 的所有剩余字符串将被替换为 ' AND '
:
s2 = s1.gsub(' +', ' AND ')
#=> "(A|B) AND * AND (E|*|*) AND (* AND (*|*)) AND Q AND R"
仍然需要用 ' OR '
替换 '|'
并且每个星号的原始值:
s2.gsub(/[*|]/) { |s| s == '|' ? ' OR ' : subs.shift }
#=> "(A OR B) AND \"C D\" AND (E OR \"F G\" OR \"H I J\") AND (\"K L\" AND (\"M N\" OR \"O P\")) AND Q AND R"
我一直在思考将以下 Sphinx 搜索查询转换为典型网络搜索或门户中常用的查询的最简单方法是什么,例如布尔搜索字符串,反之亦然
(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R
需要转换为
(A OR B) AND "C D" AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
为了示例目的也略有不同
(A | B) C D (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R
应该是
(A OR B) AND C AND D AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
为清楚起见, “A”可以是任何单词和任何大小写,不区分大小写。除非在引号内,否则空格在起始语法中表示 AND。所以 AB 只是一个词,例如Java。 (A|B) 之间的 space 不重要 (A|B) 等同于 ( A | B ) 或 (A | B) 等。每个字母表示一个单词。
其中一些查询会很长 - 最多 500 个术语。虽然这不是一个巨大的处理开销,但我在想什么是转换它的最佳(最有效)方法。标记化、Regex/pattern 匹配、简单替换、递归等。你们会推荐什么?
读者可能正在寻找一个优雅的,至少不是 hackish 的解决方案来解决这个问题。那也是我的 objective,但是,唉,这是我能想到的最好的了。
代码
def convert(str)
subs = []
str.gsub(/"[^"]*"| *\| */) do |s|
if s.match?(/ *\| */)
'|'
else
subs << s
'*'
end
end.gsub(/ +/, ' AND ').
gsub(/[*|]/) { |s| s == '|' ? ' OR ' : subs.shift }
end
例子
puts convert(%Q{(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R})
#-> (A OR B) AND "C D" AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
puts convert(%Q{(A|B) C D (E| "F G" |"H I J") ("K L" ("M N" | "O P")) Q R})
#-> (A OR B) AND C AND D AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
请注意,在此示例中,在某些管道之后的 and/or 之前没有 space,并且在双引号字符串之外的某些地方有多个 space。
puts convert(%Q{(Ant | Bat) Cat Dog (Emu | "Frog Gorilla" | "Hen Ibex Jackel") ("Khawla Lynx" ("Magpie Newt" | "Ocelot Penguin")) Quail Rabbit})
#-> (Ant OR Bat) AND Cat AND Dog AND (Emu OR "Frog Gorilla" OR "Hen Ibex Jackel") AND ("Khawla Lynx" AND ("Magpie Newt" OR "Ocelot Penguin")) AND Quail AND Rabbit
这里我用单词替换了大写字母。
说明
要了解这是如何工作的,让
str = %Q{(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R}
#=> "(A | B) \"C D\" (E | \"F G\" | \"H I J\") (\"K L\" (\"M N\" | \"O P\")) Q R"
然后
subs = []
str.gsub(/"[^"]*"| *\| */) do |s|
if s.match?(/ *\| */)
'|'
else
subs << s
'*'
end
end
#=> "(A|B) * (E|*|*) (* (*|*)) Q R"
subs
#=> ["\"C D\"", "\"F G\"", "\"H I J\"", "\"K L\"", "\"M N\"", "\"O P\""]
如您所见,我删除了管道周围的 spaces 并将所有带引号的字符串替换为星号,将这些字符串保存在数组 subs
中,以便我以后可以将星号替换为他们的原始值。星号的选择当然是随意的。
正则表达式为“匹配零个或多个字符的双引号字符串或竖线 ('|'
) 可选地在 and/or 后跟 spaces”。
作为这些替换的结果,space 的所有剩余字符串将被替换为 ' AND '
:
s2 = s1.gsub(' +', ' AND ')
#=> "(A|B) AND * AND (E|*|*) AND (* AND (*|*)) AND Q AND R"
仍然需要用 ' OR '
替换 '|'
并且每个星号的原始值:
s2.gsub(/[*|]/) { |s| s == '|' ? ' OR ' : subs.shift }
#=> "(A OR B) AND \"C D\" AND (E OR \"F G\" OR \"H I J\") AND (\"K L\" AND (\"M N\" OR \"O P\")) AND Q AND R"