【问题标题】:How to optimize repeated Regex extractions in Google BigQuery如何优化 Google BigQuery 中的重复正则表达式提取
【发布时间】:2019-12-05 13:06:19
【问题描述】:

我的表有大约 1700 万行新闻文章,总计大约 40 GB 的数据。我有一个包含大约 120 个关键字的列表,并希望在所有文章中提取它们的出现以及周围的上下文。

所以我的正则表达式部分看起来像这样:

REGEXP_EXTRACT_ALL(LOWER(body), ".{1,50}\\bKEYWORD\\b.{1,50}"

其中KEYWORD 被真正的关键字替换,body 是文章的全文。

对所有 120 个关键字执行这些查询并将结果聚合到一个目标表中的最佳策略是什么?

【问题讨论】:

    标签: sql regex performance google-bigquery


    【解决方案1】:

    以下是 BigQuery 标准 SQL

    #standardSQL
    WITH keywords AS (
      SELECT LOWER(keyword) AS keyword
      FROM UNNEST(['Car', 'Vehicle', 'Motorcycle']) keyword
    )
    SELECT REGEXP_EXTRACT_ALL(LOWER(body), keywords_regexp) AS mentions, body 
    FROM `project.dataset.table`, 
    (SELECT CONCAT(r'.{1,50}\b(?:', STRING_AGG(keyword, '|'), r')\b.{1,50}') AS keywords_regexp FROM keywords)
    WHERE REGEXP_CONTAINS(body, keywords_regexp)
    

    您可以使用一些虚拟或公共数据进行测试,如以下示例所示

    #standardSQL
    WITH `project.dataset.table` AS (
      SELECT text AS body
      FROM `bigquery-public-data.hacker_news.comments`
    ), keywords AS (
      SELECT LOWER(keyword) AS keyword
      FROM UNNEST(['Car', 'Vehicle', 'Motorcycle']) keyword
    )
    SELECT REGEXP_EXTRACT_ALL(LOWER(body), keywords_regexp) AS mentions, body 
    FROM `project.dataset.table`, 
    (SELECT CONCAT(r'.{1,50}\b(?:', STRING_AGG(keyword, '|'), r')\b.{1,50}') AS keywords_regexp FROM keywords)
    WHERE REGEXP_CONTAINS(body, keywords_regexp)
    -- LIMIT 100   
    

    更新:优化版本 - 仅用了 17-20 秒,而上述版本需要 440-460 秒

    #standardSQL
    WITH `project.dataset.table` AS (
      SELECT text AS body
      FROM `bigquery-public-data.hacker_news.comments`
    ), keywords AS (
      SELECT 
        CONCAT(r'\b', LOWER(keyword), r'\b') AS keyword_test,
        CONCAT(r'.{1,50}\b', LOWER(keyword), r'\b.{1,50}') AS keyword
      FROM UNNEST(['Car', 'Vehicle', 'Motorcycle']) keyword
    )
    SELECT ARRAY_CONCAT_AGG(mention) AS mentions, body
    FROM (
      SELECT body, REGEXP_EXTRACT_ALL(LOWER(body), keyword) AS mention
      FROM (
        SELECT keyword, body 
        FROM `project.dataset.table`, keywords 
        WHERE REGEXP_CONTAINS(body, keyword_test) 
      )
    )
    GROUP BY body   
    

    根据 OP 的要求 - 对不同之处的一些解释 :o)

    在初始版本中 - 有一种简单直接的方法,即使用所有关键字构建正则表达式并逐行应用提取所有内容,这显然非常昂贵(性能方面)

    因此,优化版本首先为每个工作流程提取合格的关键字 - 仅使用关键字,每边不包含 50 个字符。因此,在第一个“回合”中,我们收集成对的关键字和正文包含关键字的行。因此,如果 body 有 N 个关键字 - 我们将获得 N 行关键字,body。 然后,每个这样的行仅由具有给定限定关键字的完整正则表达式处理。这最终会便宜得多! 因此,最后的操作只是将具有相同主体的行组合回(分组)并聚合初始提取 - 但因为初始提取本身可能是数组 - 我们不仅使用 ARRAY_AGG 而是使用 ARRAY_CONCAT_AGG 函数

    希望这有助于理解上述优化版本的工作原理以及为什么它工作得更好:o)

    【讨论】:

    • 我今天尝试了您的解决方案,但显然它运行的时间比我最初的解决方案要长得多(这是写出查询中的所有正则表达式并将它们放在一些 STRUCT 结构中。您的查询要容易得多不过要阅读。我的第一次尝试需要 25 分钟才能完成,即使一个小时后您的解决方案也没有完成。不知道究竟为什么,我需要进一步调查
    • 是的,同意——在这种情况下“更容易”阅读和维护代码会影响性能——即使对于 3 个关键字和 840 万行的相对简单的示例——处理也需要将近 8 分钟。看起来我更专注于方向,并没有关注优化方面,而是为了保持代码的小而紧凑。所以,我想出了优化版本 - 很快就会看到我的答案中的更新 - 好消息 - 相同的 3 个关键字和 8.4M M 行只用了 20 秒
    • 我可以确认您的优化查询执行得更好。非常感谢,米哈伊尔!不过,如果你不介意,你能解释一下有什么区别吗?很想了解更多相关信息
    • @HenningLebbäus - 当然,会在一天晚些时候回复您并提供一些解释:o)
    • @HenningLebbäus - 请参阅我的回答中的更新 - 添加了“一些关于有何不同的解释”
    猜你喜欢
    • 1970-01-01
    • 2013-12-05
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多