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

Posted

技术标签:

【中文标题】将 SphinxSearch 查询语法转换为 Ruby 中的布尔搜索字符串【英文标题】:Convert SphinxSearch query syntax to boolean search string in Ruby 【发布时间】:2021-12-28 18:09:45 【问题描述】:

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

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

【问题讨论】:

【参考方案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"

【讨论】:

工作得很好卡里。我会在一些非常大的字符串上给它一个 bash - 但我认为从逻辑上这应该是防弹的。这是否是“最有效”的方式——我敢肯定有人要加几分钱,但我认为这是非常有效和干净的。好! 请告诉我结果如何。

以上是关于将 SphinxSearch 查询语法转换为 Ruby 中的布尔搜索字符串的主要内容,如果未能解决你的问题,请参考以下文章

将 SQL 连接查询转换为 pyspark 语法

如何将此 SQL 内部联接查询转换为 LINQ 语法?

子查询的GroupBy方法语法

如何将以下 SQL 语句转换为 LINQ 方法语法

NHibernate 将 sql 查询转换为 NHibernate QueryOver 查询

将 mysql 查询转换为 laravel 查询生成器