Bigquery: извлечение данных из массива json - PullRequest
1 голос
/ 16 февраля 2020

(это расширение этого вопроса , но моя репутация слишком низкая, чтобы комментировать или задавать дополнительные вопросы по этой теме c ...)

Мы работаем над bigquery следовательно, ограничен в импорте пакетов или использовании других языков. И, согласно ссылке выше, js - это решение, но не то, что я ищу здесь. Я реализовал его в js, и он был слишком медленным для наших нужд.

Предположим, что один из наших столбцов - это строка, которая выглядит следующим образом (массив json):
[{"location":[22.99902,66.000],"t":1},{"location":[55.32168,140.556],"t":2},{"location":[85.0002,20.0055],"t":3}]

Я хочу извлечь из столбца json, для которого "t": 2

Где:

  • В некоторых столбцах нет элементов "t" : 2
  • В некоторых столбцах есть несколько элементов "t": 2
  • Количество json элементов в каждой строке может измениться
  • элемент "t": 2 не является всегда на второй позиции.

Я недостаточно хорошо знаю регулярное выражение для этого. Мы попробовали regexp_extract с этим шаблоном: r'(\{.*?\"t\":2.*?\})')), но это не работает. Он извлекает все, что предшествует "t": 2, включая json для "t": 2. Нам нужен только json элемента "t": 2.

Не могли бы вы посоветовать шаблон регулярного выражения, который будет работать?

РЕДАКТИРОВАТЬ:

Я предпочитаю решение, которое дает мне 1 матч. Предположим, у меня есть эта строка: [{"location":[22.99902,66.000],"t":1},{"location":[55.32168,140.556],"t":2},{"location":[55.33,141.785],"t":2}],
Я бы предпочел получить только 1 ответ, первый.

В этом случае, возможно, регулярное выражение менее подходит, но я действительно не уверен?

Ответы [ 4 ]

1 голос
/ 16 февраля 2020

Как насчет этого:

(?<=\{)(?=.*?\"t\"\s*:\s*2).*?(?=\})

Как видно здесь

0 голосов
/ 17 февраля 2020

Несмотря на то, что вы опубликовали работу по вашей проблеме. Я считаю, что этот ответ будет информативным. Вы упомянули, что один из ответов был выбран больше, чем вам нужно, я написал запрос ниже, чтобы воспроизвести ваш случай и добиться намеченного результата.

  WITH
  data AS (
  SELECT
    " [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":2},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j
  UNION ALL
  SELECT
    " [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":3},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j
  UNION ALL
  SELECT
    " [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":3},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j
  UNION ALL
  SELECT
    " [{ \"location\":[22.99902,66.000]\"t\":1},{\"location\":[55.32168,140.556],\"t\":3},{\"location\":[85.0002,20.0055],\"t\":3}] " AS string_j ),
  refined_data AS (
  SELECT
    REGEXP_EXTRACT(string_j, r"\{\"\w*\"\:\[\d*\.\d*\,\d*\.\d*\]\,\"t\"\:2\}") AS desired_field
  FROM
    data )
  SELECT
  *
  FROM
  refined_data
  WHERE
  desired_field IS NOT NULL

Обратите внимание, что я использовал манекен, описанный в временной таблице, заполняется внутри WITH метода. Как показано ниже:

enter image description here

Afterwords, в таблице refined_data я использовал REGEXP_EXTRACT для извлечения желаемая строка из столбца. Обратите внимание, что для строк, для которых нет выражения совпадения, выдается null . Таким образом, таблица refined_data выглядит следующим образом:

enter image description here

Как видите, сейчас просто нужно просто WHERE фильтр для получения желаемого результата, который был сделан при последнем выборе.

Кроме того, вы можете увидеть информацию о предоставленном мною выражении здесь .

Надеюсь, это поможет.

0 голосов
/ 17 февраля 2020

Существует другое решение, но оно не основано на регулярных выражениях (как я изначально просил). Так что это не должно считаться окончательным ответом на мой собственный вопрос, тем не менее, может быть полезным.
Он основан на разбиении строки в массиве и последующем выборе элемента в массиве, который удовлетворяет моим потребностям.

Шаги:

  1. преобразовать строку в нечто лучшее для разбиений (используя '|' в качестве разделителя):
    replace(replace(replace(my_field,'},{','}|{'),'[{','{'),'}]','}')
  2. разбить его, используя split(), что дает массив строк (каждая из которых json элемент)
  3. найти соответствующий элемент ("t": 2) - в моем случае первый достаточно хорошо, поэтому я ограничиваю запрос 1: array( select data from unnest(split(replace(replace(replace(my_field,'},{','}|{'),'[{','{'),'}]','}'),'|')) as data where data like '%"t":2%' limit 1)
  4. . Преобразуйте это в пригодную для использования строку с помощью array_to_string () и используйте json_extract для этой строки, чтобы извлечь соответствующую информацию из нужного мне элемента (скажем, для Например, координата местоположения х).

Итак, все вместе:
round(safe_cast(json_extract(array_to_string(array( select data from unnest(split(replace(replace(replace(my_field,'},{','}|{'),'[{','{'),'}]','}'),'|')) as data where data like '%"t":2%' limit 1),''),'$.location[0]') as float64),3) loc_x

0 голосов
/ 16 февраля 2020

1 мая 2020 г. Обновление

Новая функция JSON_EXTRACT_ARRAY была добавлена ​​в список JSON функций. Эта функция позволяет извлекать содержимое документа JSON в виде массива строк.

, поэтому ниже вы можете заменить использование json2array UDF только встроенной функцией JSON_EXTRACT_ARRAY как в примере ниже

#standardSQL
SELECT id,  
  (
    SELECT x
    FROM UNNEST(JSON_EXTRACT_ARRAY(json, '$')) x
    WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
  ) extracted
FROM `project.dataset.table`  

==============

Ниже приведено описание BigQuery Standard SQL

#standardSQL
CREATE TEMP FUNCTION json2array(json STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
  return JSON.parse(json).map(x=>JSON.stringify(x));
"""; 
SELECT id,  
  (
    SELECT x
    FROM UNNEST(json2array(JSON_EXTRACT(json, '$'))) x
    WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
  ) extracted
FROM `project.dataset.table`  

Вы можете проверить, поиграть с выше, используя фиктивные данные, как в примере ниже

#standardSQL
CREATE TEMP FUNCTION json2array(json STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
  return JSON.parse(json).map(x=>JSON.stringify(x));
"""; 
WITH `project.dataset.table` AS (
  SELECT 1 id, '[{"location":[22.99902,66.000],"t":1},{"location":[55.32168,140.556],"t":2},{"location":[85.0002,20.0055],"t":3}]' json UNION ALL
  SELECT 2, '[{"location":[22.99902,66.000],"t":11},{"location":[85.0002,20.0055],"t":13}]'
)
SELECT id,  
  (
    SELECT x
    FROM UNNEST(json2array(JSON_EXTRACT(json, '$'))) x
    WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
  ) extracted
FROM `project.dataset.table`

с выводом

Row id  extracted    
1   1   {"location":[55.32168,140.556],"t":2}    
2   2   null     

Выше предполагается, что не более одного элемента с "t":2 в столбце json. В случае, если их может быть больше одного - вы должны добавить ARRAY, как показано ниже

SELECT id,  
  ARRAY(
    SELECT x
    FROM UNNEST(json2array(JSON_EXTRACT(json, '$'))) x
    WHERE JSON_EXTRACT_SCALAR(x, '$.t') = '2'
  ) extracted
FROM `project.dataset.table`
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...