From 18cca949621bfb73b669ce89eeff085a754161ae Mon Sep 17 00:00:00 2001 From: Jennifer Davis Date: Tue, 3 Nov 2026 23:31:53 +0000 Subject: [PATCH] feat(bigquery): add bigquery_vector_search_brute_force sample to run exact brute-force vector search Problem: The BigQuery documentation (https://cloud.google.com/bigquery/docs/vector-search) needs a standalone, tested Python code sample demonstrating how to run exact brute-force vector search (region tag `bigquery_vector_search_brute_force`). Solution: - Add `vector_search_brute_force(project_id: str) -> None` in `bigquery/vector_search/bigquery_vector_search_brute_force.py` (Compute exact nearest-neighbor distances over embeddings using VECTOR_SEARCH with use_brute_force => TRUE.) - Add unit tests (`bigquery/vector_search/bigquery_vector_search_brute_force_test.py`) covering both the success path and API error handling (`GoogleAPICallError`) --- .../bigquery_vector_search_brute_force.py | 97 +++++++++++++++++++ ...bigquery_vector_search_brute_force_test.py | 58 +++++++++++ bigquery/vector_search/noxfile_config.py | 23 +++++ bigquery/vector_search/requirements-test.txt | 1 + bigquery/vector_search/requirements.txt | 1 + 5 files changed, 180 insertions(+) create mode 100644 bigquery/vector_search/bigquery_vector_search_brute_force.py create mode 100644 bigquery/vector_search/bigquery_vector_search_brute_force_test.py create mode 100644 bigquery/vector_search/noxfile_config.py create mode 100644 bigquery/vector_search/requirements-test.txt create mode 100644 bigquery/vector_search/requirements.txt diff --git a/bigquery/vector_search/bigquery_vector_search_brute_force.py b/bigquery/vector_search/bigquery_vector_search_brute_force.py new file mode 100644 index 00000000000..c769110a7c2 --- /dev/null +++ b/bigquery/vector_search/bigquery_vector_search_brute_force.py @@ -0,0 +1,97 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START bigquery_vector_search_brute_force] +import sys + +from google.api_core import exceptions +from google.cloud import bigquery + + +def vector_search_brute_force( + project_id: str, +) -> None: + """Runs an exact brute-force vector similarity search over BBC news embeddings. + + Args: + project_id: Google Cloud project ID used to bill the query job + (for example, 'example-project'). + """ + client = bigquery.Client(project=project_id) + # Query the public BBC News dataset in bigquery-public-data + # (https://cloud.google.com/bigquery/public-data). + table_ref = "bigquery-public-data.bbc_news.fulltext" + # Pass a 768-dimensional query vector and enable use_brute_force for exact distance calculation. + category_filter = "tech" + query_embedding = [0.01] * 768 + job_config = bigquery.QueryJobConfig( + query_parameters=[ + bigquery.ScalarQueryParameter("category_filter", "STRING", category_filter), + bigquery.ArrayQueryParameter("query_embedding", "FLOAT64", query_embedding), + ] + ) + sql = f""" + SELECT + base.title AS matched_title, + base.category AS matched_category, + distance + FROM VECTOR_SEARCH( + ( + SELECT + title, + category, + body_embedded + FROM `{table_ref}` + WHERE category = @category_filter + AND ARRAY_LENGTH(body_embedded) = 768 + ), + 'body_embedded', + (SELECT @query_embedding AS query_embedding), + query_column_to_search => 'query_embedding', + top_k => 5, + distance_type => 'COSINE', + options => '{{"use_brute_force": true}}' + ) + ORDER BY distance ASC, matched_title ASC; + """ + + try: + print( + f"Running brute-force vector search on {table_ref} (category={category_filter})" + ) + query_job = client.query(sql, job_config=job_config) + rows = list(query_job.result()) + except exceptions.NotFound as err: + print( + f"error: public embeddings table '{table_ref}' not found when querying " + f"from project '{project_id}': {err.message}", + file=sys.stderr, + ) + return + except exceptions.GoogleAPICallError as err: + print( + f"error: failed to run brute-force vector search on '{table_ref}' in " + f"project '{project_id}': {err.message}", + file=sys.stderr, + ) + return + + print(f"Resource ID: {table_ref}") + print("Distance Type: COSINE") + print(f"Embedding Dimensions: {len(query_embedding)}") + print(f"Job ID: {query_job.job_id}") + print(f"Neighbors Returned: {len(rows)}") + + +# [END bigquery_vector_search_brute_force] diff --git a/bigquery/vector_search/bigquery_vector_search_brute_force_test.py b/bigquery/vector_search/bigquery_vector_search_brute_force_test.py new file mode 100644 index 00000000000..346262804c9 --- /dev/null +++ b/bigquery/vector_search/bigquery_vector_search_brute_force_test.py @@ -0,0 +1,58 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import mock + +import bigquery_vector_search_brute_force +from google.api_core import exceptions +import pytest + + +def test_vector_search_brute_force_success( + capsys: pytest.CaptureFixture[str], +) -> None: + with mock.patch("google.cloud.bigquery.Client") as mock_client_cls: + mock_client = mock_client_cls.return_value + mock_job = mock.MagicMock() + mock_job.job_id = "example-job-123" + mock_job.result.return_value = [mock.MagicMock() for _ in range(5)] + mock_client.query.return_value = mock_job + + bigquery_vector_search_brute_force.vector_search_brute_force( + project_id="example-project", + ) + + mock_client.query.assert_called_once() + captured = capsys.readouterr() + assert "Resource ID: bigquery-public-data.bbc_news.fulltext" in captured.out + assert "Embedding Dimensions: 768" in captured.out + assert "Neighbors Returned: 5" in captured.out + + +def test_vector_search_brute_force_error( + capsys: pytest.CaptureFixture[str], +) -> None: + with mock.patch("google.cloud.bigquery.Client") as mock_client_cls: + mock_client = mock_client_cls.return_value + mock_job = mock.MagicMock() + mock_job.result.side_effect = exceptions.NotFound("Table not found") + mock_client.query.return_value = mock_job + + bigquery_vector_search_brute_force.vector_search_brute_force( + project_id="example-project", + ) + + captured = capsys.readouterr() + assert "error:" in captured.err + assert "bbc_news.fulltext" in captured.err diff --git a/bigquery/vector_search/noxfile_config.py b/bigquery/vector_search/noxfile_config.py new file mode 100644 index 00000000000..63902aa8c7f --- /dev/null +++ b/bigquery/vector_search/noxfile_config.py @@ -0,0 +1,23 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + "ignored_versions": ["2.7", "3.7", "3.8", "3.9", "3.10", "3.11"], + # Enforce type hints on new samples. + "enforce_type_hints": True, + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + "pip_version_override": None, + "envs": {}, +} diff --git a/bigquery/vector_search/requirements-test.txt b/bigquery/vector_search/requirements-test.txt new file mode 100644 index 00000000000..c9e154ba440 --- /dev/null +++ b/bigquery/vector_search/requirements-test.txt @@ -0,0 +1 @@ +pytest==9.0.3; python_version >= "3.10" diff --git a/bigquery/vector_search/requirements.txt b/bigquery/vector_search/requirements.txt new file mode 100644 index 00000000000..dc6cf8e4329 --- /dev/null +++ b/bigquery/vector_search/requirements.txt @@ -0,0 +1 @@ +google-cloud-bigquery==3.47.0