将SphinxSearch查询语法转换为Ruby中的布尔搜索字符串



我一直在思考将以下Sphinx Search查询转换为典型网络搜索或门户网站中常用的查询的最简单方法,例如布尔搜索字符串,反之亦然

(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(之间的空格并不重要(A|B(与(A|乙(或(A|乙方(等相同。每个字母表示一个单词。

其中一些查询将相当长,最多可达500个术语。虽然这不是一个巨大的处理开销,但我在想什么是转换它的最佳(最有效(方式。标记化、Regex/模式匹配、简单替换、递归等。你们有什么建议吗?

读者可能正在寻找一个优雅的、至少不是黑客的解决方案来解决这个问题。这也是我的目标,但是,唉,这是我能想到的最好的。

代码

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

请注意,在本例中,在某些管道之前和/或之后没有空格,在双引号字符串之外的某些地方有多个空格。

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""]

正如您所看到的,我已经删除了管道周围的空格,并用星号替换了所有引用的字符串,将这些字符串保存在数组subs中,以便以后可以用它们的原始值替换星号。星号的选择当然是任意的。

正则表达式读作";匹配零个或多个字符的双引号字符串或可选地在前面和/或后面加空格的管道('|'(;。

作为这些替换的结果,所有剩余的空格串都将被' 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"

最新更新