Как было указано в моем вопросе, я работал над версией, использующей UDF JavaScript, которая решает эту задачу, хотя и медленнее, чем ответ, который я принял. Для полноты, я публикую это здесь, потому что, возможно, кто-то (как я в будущем) может найти это полезным.
CREATE TEMPORARY FUNCTION CONTAINS_ANY(str STRING, fragments ARRAY<STRING>)
RETURNS STRING
LANGUAGE js AS """
for (var i in fragments) {
if (str.indexOf(fragments[i]) >= 0) {
return fragments[i];
}
}
return null;
""";
WITH record AS (
SELECT text AS name
FROM `bigquery-public-data.hacker_news.comments`
WHERE text IS NOT NULL
), fragment AS (
SELECT name AS name, COUNT(*)
FROM `bigquery-public-data.usa_names.usa_1910_current`
WHERE name IS NOT NULL
GROUP BY name
), fragment_array AS (
SELECT ARRAY_AGG(name) AS names, COUNT(*) AS count
FROM fragment
GROUP BY LENGTH(name)
), records_with_fragments AS (
SELECT record.name,
CONTAINS_ANY(record.name, fragment_array.names)
AS fragment_name
FROM record INNER JOIN fragment_array
ON CONTAINS_ANY(name, fragment_array.names) IS NOT NULL
)
SELECT * EXCEPT(rownum) FROM (
SELECT record.name,
records_with_fragments.fragment_name,
ROW_NUMBER() OVER (PARTITION BY record.name) AS rownum
FROM record
INNER JOIN records_with_fragments
ON records_with_fragments.name = record.name
AND records_with_fragments.fragment_name IS NOT NULL
) WHERE rownum = 1
Идея состоит в том, что список фрагментов достаточно мал, чтобы его можно было обрабатывать в массиве, аналогично ответу Фелипе с использованием регулярных выражений. Первое, что я делаю, это создаю таблицу fragment_array
, сгруппированную по длинам фрагментов ... дешевый способ предотвращения массива слишком большого размера , который, как я обнаружил, , может вызвать тайм-ауты UDF.
Затем я создаю таблицу с именем records_with_fragments
, которая соединяет эти массивы с исходными записями, находя только те, которые содержат соответствующий фрагмент, используя JavaScript UDF CONTAINS_ANY()
. Это приведет к таблице, содержащей несколько дубликатов, поскольку одна запись может соответствовать нескольким фрагментам.
Последний SELECT
затем извлекает исходную таблицу record
, присоединяется к records_with_fragments
, чтобы определить, какой фрагмент соответствует, а также использует функцию ROW_NUMBER()
для предотвращения дублирования, например, показывает только первый ряд каждой записи, как однозначно идентифицируется ее name
.
Теперь, причина, по которой я выполняю объединение в последнем запросе, заключается в том, что в моих фактических данных есть еще поля, которые я хочу, кроме сопоставляемой строки. Ранее в моих фактических данных я создал таблицу из DISTINCT
строк, которые затем нужно будет снова объединить.
Вуаля! Не самый элегантный, но он выполняет свою работу.