0

我一直在思考将以下 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) 之间的空格不重要 (A|B) 与 (A | B) 或 (A | B) 等相同。每个字母表示一个单词。

其中一些查询将很长 - 最多 500 个术语。虽然这不是一个巨大的处理开销,但我在想什么是最好的(最有效的)转换方式。标记化、正则表达式/模式匹配、简单替换、递归等。你们会推荐什么?

4

1 回答 1

1

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

代码

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"
于 2021-11-17T20:52:58.640 回答