首页 > 解决方案 > BYTES 的按位二元运算符要求输入的长度相等。左侧有 16 个字节,右侧有 4 个字节

问题描述

我正在尝试使用他们的 IP 将用户与他们的地理位置相匹配。

IPs 是 ipv4、ipv6 和一些带有无效条目的行的混合。

我使用 Felipe Hoffa https://towardsdatascience.com/geolocation-with-bigquery-de-identify-76-million-ip-addresses-in-20-seconds-e9e652480bd2的这篇文章中的说明将我的 IP 与块文件。

问题是尝试使用 NET.SAFE_IP_FROM_STRING(ip_address) 和 NET.IP_NET_MASK(4,mask) 函数时出现错误。错误是:

BYTES 的按位二元运算符要求输入的长度相等。左侧有 16 个字节,右侧有 4 个字节。

所以我尝试在使用正则表达式将 IP 传递给函数之前确保它们是有效的。这适用于 ipv4。我现在也想出了匹配 ipv6 的正则表达式,从我的检查来看,它似乎是准确的。但是我仍然得到错误。我不知道为什么以及如何修复我的查询以获得正确的结果。

请参阅下面的整个查询:

(
    SELECT
      *,
      NET.SAFE_IP_FROM_STRING(ip_address) & NET.IP_NET_MASK(4,
        mask) network_bin
    FROM (
      SELECT
        * EXCEPT (is_valid)
      FROM (
        SELECT
          *,
          CASE
            WHEN (REGEXP_CONTAINS(ip_address, r'\A[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}\z') OR (NOT REGEXP_CONTAINS(ip_address, r'\A(.*?[a-f0-9](:|\z)){8}') AND REGEXP_CONTAINS(ip_address, r'\A([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,6})?::([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,6})?\z')) OR REGEXP_CONTAINS(ip_address, r'\A[a-f0-9]{1,4}(:[a-f0-9]{1,4}){5}:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z') OR (NOT REGEXP_CONTAINS(ip_address, r'\A(.*?[a-f0-9]:){6}') AND REGEXP_CONTAINS(ip_address, r'\A([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,4})?::([a-f0-9]{1,4}:){0,5}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z')) ) THEN TRUE
            WHEN REGEXP_CONTAINS(ip_address, r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$") THEN TRUE
          ELSE
          FALSE
        END
          AS is_valid
        FROM (
          SELECT
            user,
            ip_address,
            date
          FROM 
            `project.dataset.table`)
      WHERE
        is_valid IS TRUE),
      UNNEST(GENERATE_ARRAY(9,32)) mask)

标签: google-bigquery

解决方案


问题可能是 NET.SAFE_IP_FROM_STRING 返回 4 个字节的 IPv4 和 16 个字节的 IPv6。& NET.IP_NET_MASK(4, mask)IPv4 也可以,但对于 IPv6,您需要使用& NET.IP_NET_MASK(16, mask). 一种选择是引入类似的东西并像这样is_ipv4使用它:

IF(
  is_ipv4,
  NET.SAFE_IP_FROM_STRING(ip_address) & NET.IP_NET_MASK(4, mask),
  NET.SAFE_IP_FROM_STRING(ip_address) & NET.IP_NET_MASK(16, mask)
) network_bin

推荐阅读