繁体   English   中英

在ruby中使用正则表达式拆分数据库查询字符串

[英]Split a database query string using regex in ruby

我有一个要分离的查询字符串

created_at BETWEEN '2018-01-01T00:00:00+05:30' AND '2019-01-01T00:00:00+05:30' AND updated_at BETWEEN '2018-05-01T00:00:00+05:30' AND '2019-05-01T00:00:00+05:30' AND user_id = 5 AND status = 'closed'

像这样

created_at BETWEEN '2018-01-01T00:00:00+05:30' AND '2019-01-01T00:00:00+05:30'

updated_at BETWEEN '2018-05-01T00:00:00+05:30' AND '2019-05-01T00:00:00+05:30'

user_id = 5

status = 'closed'

这只是一个示例字符串,我想动态分离查询字符串。 我知道不能只用AND分割AND因为BETWEEN .. AND

您也许可以使用regex做到这一点,但是这里有一个解析器可能适用于您的用例。 当然可以改进它,但是应该可以。

require 'time'

def parse(sql)
  arr = []
  split = sql.split(' ')
  date_counter = 0
  split.each_with_index do |s, i|
    date_counter = 2 if s == 'BETWEEN'
    time = Time.parse(s.strip) rescue nil
    date_counter -= 1 if time
    arr << i+1 if date_counter == 1
  end
  arr.select(&:even?).each do |index|
    split.insert(index + 2, 'SPLIT_ME')
  end
  split = split.join(' ').split('SPLIT_ME').map{|l| l.strip.gsub(/(AND)$/, '')}
  split.map do |line|
    line[/^AND/] ? line.split('AND') : line
  end.flatten.select{|l| !l.empty?}.map(&:strip)
end

这不是真正的正则表达式,而是一个简单的解析器。

  1. 这是通过,直到遇到一个空格,然后要么匹配从字符串的开头一个正则表达式andbetween接空格字符。 结果从where_cause删除,并保存在statement
  2. 如果字符串的开头现在以空格开头,然后是, between是空格。 它添加到statement然后从where_cause删除,之后where_cause上1 and 如果达到或另一个字符串的结尾匹配停止and遇到。
  3. 如果第2点不匹配,请检查字符串是否以空格开头and后跟一个空格。 如果是这种情况,请从where_cause删除它。
  4. 最后补充statementstatements数组,如果它不是一个空字符串。

所有匹配均不区分大小写。

where_cause = "created_at BETWEEN '2018-01-01T00:00:00+05:30' AND '2019-01-01T00:00:00+05:30' AND updated_at BETWEEN '2018-05-01T00:00:00+05:30' AND '2019-05-01T00:00:00+05:30' AND user_id = 5 AND status = 'closed'"

statements = []
until where_cause.empty?
  statement = where_cause.slice!(/\A.*?(?=[\s](and|between)[\s]|\z)/mi)

  if where_cause.match? /\A[\s]between[\s]/i
    between = /\A[\s]between[\s].*?[\s]and[\s].*?(?=[\s]and[\s]|\z)/mi
    statement << where_cause.slice!(between)
  elsif where_cause.match? /\A[\s]and[\s]/i
    where_cause.slice!(/\A[\s]and[\s]/i)
  end

  statements << statement unless statement.empty?
end

pp statements
# ["created_at BETWEEN '2018-01-01T00:00:00+05:30' AND '2019-01-01T00:00:00+05:30'",
#  "updated_at BETWEEN '2018-05-01T00:00:00+05:30' AND '2019-05-01T00:00:00+05:30'",
#  "user_id = 5",
#  "status = 'closed'"]

注意: Ruby使用\\A来匹配字符串的开头,并使用\\z来匹配字符串的结尾,而不是通常的^$ ,它们分别匹配行的开头和结尾。 请参阅regexp锚文档

如果愿意,可以将每个[\\s]替换为\\s 我添加了它们以使正则表达式更具可读性。

请记住,该解决方案并不完美,但可能会给您一个解决问题的思路。 我之所以说这是因为它不占字and / between的列名或字符串上下文。

以下是原因所在:

where_cause = "name = 'Tarzan AND Jane'"

将输出:

#=> ["name = 'Tarzan", "Jane'"]

该解决方案还假定结构正确的SQL查询。 以下查询不会导致您的想法:

where_cause = "created_at = BETWEEN AND"
# TypeError: no implicit conversion of nil into String
# ^ does match /\A[\s]between[\s]/i, but not the #slice! argument 
where_cause = "id = BETWEEN 1 AND 2 BETWEEN 1 AND 3"
#=> ["id = BETWEEN 1 AND 2 BETWEEN 1", "3"]

我不确定我是否理解这个问题,尤其是考虑到先前的答案,但是如果您只是想从字符串中提取指示的子字符串,并且所有列名都以小写字母开头,则可以编写以下内容(其中str持有问题中给出的字符串):

str.split(/ +AND +(?=[a-z])/)
  #=> ["created_at BETWEEN '2018-01-01T00:00:00+05:30' AND '2019-01-01T00:00:00+05:30'",
  #    "updated_at BETWEEN '2018-05-01T00:00:00+05:30' AND '2019-05-01T00:00:00+05:30'",
  #    "user_id = 5",
  #    "status = 'closed'"]

正则表达式为:“匹配一个或多个空格,后跟'AND' ,后跟一个或多个空格,后跟一个包含小写字母的正向前行”。 前瞻性为正,小写字母不属于所返回匹配项的一部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM