Skip to content

Commit 9dcdf2e

Browse files
committed
feat(bigquery): add bigquery_search_with_text_analyzer sample to search text using a specified text analyzer
Problem: The BigQuery documentation (https://cloud.google.com/bigquery/docs/search) needs a standalone, tested Python code sample demonstrating how to search text using a specified text analyzer (region tag `bigquery_search_with_text_analyzer`). Solution: - Add `search_with_text_analyzer(project_id: str) -> None` in `bigquery/search/bigquery_search_with_text_analyzer.py` (Run a BigQuery SEARCH query specifying an explicit text analyzer such as LOG_ANALYZER, NO_OP_ANALYZER, or PATTERN_ANALYZER.) - Add unit tests (`bigquery/search/bigquery_search_with_text_analyzer_test.py`) covering both the success path and API error handling (`GoogleAPICallError`)
1 parent 186be98 commit 9dcdf2e

5 files changed

Lines changed: 174 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START bigquery_search_with_text_analyzer]
16+
import sys
17+
18+
from google.api_core import exceptions
19+
from google.cloud import bigquery
20+
21+
22+
def search_with_text_analyzer(
23+
project_id: str,
24+
) -> None:
25+
"""Searches a BigQuery column using an explicit text analyzer in SEARCH.
26+
27+
Args:
28+
project_id: Google Cloud project ID used to bill the query job
29+
(for example, 'example-project').
30+
"""
31+
client = bigquery.Client(project=project_id)
32+
# Query the public theLook eCommerce dataset in bigquery-public-data
33+
# (https://cloud.google.com/bigquery/public-data).
34+
table_ref = "bigquery-public-data.thelook_ecommerce.users"
35+
# Specify NO_OP_ANALYZER so SEARCH performs exact case-sensitive substring matching without tokenizing.
36+
analyzer_name = "NO_OP_ANALYZER"
37+
search_query = "Nelson Apt"
38+
job_config = bigquery.QueryJobConfig(
39+
query_parameters=[
40+
bigquery.ScalarQueryParameter("search_query", "STRING", search_query),
41+
]
42+
)
43+
sql = f"""
44+
SELECT
45+
u.id,
46+
u.first_name,
47+
u.last_name,
48+
u.street_address
49+
FROM `{table_ref}` AS u
50+
WHERE SEARCH(u.street_address, @search_query, analyzer => '{analyzer_name}')
51+
ORDER BY u.id ASC
52+
LIMIT 10;
53+
"""
54+
55+
try:
56+
print(
57+
f"Searching {table_ref} with analyzer {analyzer_name} for: {search_query}"
58+
)
59+
query_job = client.query(sql, job_config=job_config)
60+
rows = list(query_job.result())
61+
except exceptions.NotFound as err:
62+
print(
63+
f"error: table '{table_ref}' not found when querying from project "
64+
f"'{project_id}': {err.message}",
65+
file=sys.stderr,
66+
)
67+
return
68+
except exceptions.GoogleAPICallError as err:
69+
print(
70+
f"error: failed to search '{table_ref}' with analyzer '{analyzer_name}' "
71+
f"in project '{project_id}': {err.message}",
72+
file=sys.stderr,
73+
)
74+
return
75+
76+
print(f"Resource ID: {table_ref}")
77+
print(f"Analyzer: {analyzer_name}")
78+
print(f"Job ID: {query_job.job_id}")
79+
print(f"Matching Rows: {len(rows)}")
80+
81+
82+
# [END bigquery_search_with_text_analyzer]
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from unittest import mock
16+
17+
import bigquery_search_with_text_analyzer
18+
from google.api_core import exceptions
19+
import pytest
20+
21+
22+
def test_search_with_text_analyzer_success(
23+
monkeypatch: pytest.MonkeyPatch,
24+
capsys: pytest.CaptureFixture[str],
25+
) -> None:
26+
mock_client = mock.MagicMock()
27+
mock_job = mock.MagicMock()
28+
mock_job.job_id = "example-job-123"
29+
mock_job.result.return_value = []
30+
mock_client.query.return_value = mock_job
31+
monkeypatch.setattr(
32+
bigquery_search_with_text_analyzer.bigquery,
33+
"Client",
34+
lambda project=None: mock_client,
35+
)
36+
37+
bigquery_search_with_text_analyzer.search_with_text_analyzer(
38+
project_id="example-project",
39+
)
40+
41+
captured = capsys.readouterr()
42+
assert "Resource ID: bigquery-public-data.thelook_ecommerce.users" in captured.out
43+
assert "Analyzer: NO_OP_ANALYZER" in captured.out
44+
assert mock_client.query.called
45+
46+
47+
def test_search_with_text_analyzer_error(
48+
monkeypatch: pytest.MonkeyPatch,
49+
capsys: pytest.CaptureFixture[str],
50+
) -> None:
51+
mock_client = mock.MagicMock()
52+
mock_job = mock.MagicMock()
53+
mock_job.result.side_effect = exceptions.NotFound("resource error")
54+
mock_client.query.return_value = mock_job
55+
monkeypatch.setattr(
56+
bigquery_search_with_text_analyzer.bigquery,
57+
"Client",
58+
lambda project=None: mock_client,
59+
)
60+
61+
bigquery_search_with_text_analyzer.search_with_text_analyzer(
62+
project_id="example-project",
63+
)
64+
65+
captured = capsys.readouterr()
66+
assert "error:" in captured.err
67+
assert "example-project" in captured.err

‎bigquery/search/noxfile_config.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
TEST_CONFIG_OVERRIDE = {
16+
# You can opt out from the test for specific Python versions.
17+
"ignored_versions": ["2.7", "3.7", "3.8", "3.9", "3.10", "3.11"],
18+
# Enforce type hints on new samples.
19+
"enforce_type_hints": True,
20+
"gcloud_project_env": "GOOGLE_CLOUD_PROJECT",
21+
"pip_version_override": None,
22+
"envs": {},
23+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest==9.0.3; python_version >= "3.10"

‎bigquery/search/requirements.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
google-cloud-bigquery==3.47.0

0 commit comments

Comments
 (0)