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

时间:2019-07-07 10:42:15

标签: ruby-on-rails regex 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

这样的模式而与BETWEEN .. AND分手

3 个答案:

答案 0 :(得分:1)

您也许可以使用正则表达式来执行此操作,但是这里有一个解析器可能适用于您的用例。当然可以改进,但应该可以。

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 :(得分:1)

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

  1. 这是通过从字符串的开头匹配一个正则表达式来工作的,直到遇到一个空格,后跟andbetween后跟一个空格字符。结果从where_cause中删除,并保存在statement中。
  2. 如果字符串的开头现在以空格开头,后跟between,然后是空格。它被添加到statement并从where_cause中删除,之后再加上其他任何内容,允许1个and。如果到达字符串的末尾或遇到另一个and,则匹配停止。
  3. 如果第2点不匹配,请检查字符串是否以空格开头,后跟and,再后面是空格。如果是这种情况,请从where_cause中删除它。
  4. 如果不是空字符串,则最后将statement添加到statements数组中。

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

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来匹配字符串的结尾,而不是通常的^和{ {1}},分别与一行的开头和结尾匹配。参见regexp anchor documentation

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

请记住,此解决方案并不完美,但可能会为您提供解决问题的思路。我之所以这样说,是因为它没有考虑列名或字符串上下文中的单词\s / and

以下原因:

between

将输出:

where_cause = "name = 'Tarzan AND Jane'"

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

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

答案 2 :(得分:1)

我不确定我是否理解这个问题,特别是鉴于先前的答案,但是如果您只是想从字符串中提取指示的子字符串,并且所有列名都以小写字母开头,则可以编写以下内容(其中LoginWith2fa.cshtml.cs包含问题中给出的字符串):

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'"] ,后跟一个或多个空格,后跟一个包含小写字母的正向查找”。前瞻性为正,小写字母不属于所返回匹配项的一部分。