blob: 01b53e067c8c87c3309b507cf85f927530a09879 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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
#
# http://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.mock import patch
import pytest
ray = pytest.importorskip("ray")
from pypaimon.tests import ray_vector_search_test as fixtures
from pypaimon.multimodal.query import _PreFilterQuery
ray_cluster = fixtures.ray_cluster
table = fixtures.table
@pytest.mark.parametrize("execution", ["local", "ray"])
@pytest.mark.parametrize("scores", [False, True])
def test_distributed_lookup_and_snapshot(table, ray_cluster, execution, scores):
fixtures.add_rows(table, fixtures.VECTORS[:3])
fixtures.add_rows(table, fixtures.VECTORS[3:], 3)
query = table.search([1., 1.]).select(["id", "category"]).limit(4)
if scores:
query.with_score().order_by_score()
expected = query.to_list()
with patch.object(_PreFilterQuery, "_read_global_index_result", side_effect=AssertionError("driver lookup")):
dataset = query.to_ray(execution=execution, concurrency=2, override_num_blocks=2)
table.delete("id = 0")
table.update("id = 1", {"category": "changed"})
rows = dataset.take_all()
if scores:
assert rows == expected
else:
assert sorted(rows, key=lambda r: r["id"]) == sorted(expected, key=lambda r: r["id"])
assert dataset.schema().names == (["id", "category", "_score"] if scores else ["id", "category"])
def test_filters_empty_scores_and_metadata_only(table, ray_cluster):
fixtures.add_rows(table, fixtures.VECTORS)
query = table.search([1., 1.], pre_filter="category = 'yes'").select(["id"]).with_score().order_by_score()
assert query.to_ray().take_all() == query.to_list()
query.where("id = -1")
dataset = query.to_ray()
assert dataset.take_all() == []
assert dataset.schema().names == ["id", "_score"]
query = table.search([1., 1.]).select(["_ROW_ID"]).with_score().order_by_score().limit(3)
assert query.to_ray().take_all() == query.to_list()
def test_batch_search_datasets(table, ray_cluster):
fixtures.add_rows(table, fixtures.VECTORS)
query = table.search_vectors([[1., 1.], [4., 1.]]).select(["id"]).with_score().order_by_score().limit(2)
assert [ds.take_all() for ds in query.to_ray()] == query.to_list()
def test_blob_descriptors_can_be_resolved_on_workers(tmp_path, ray_cluster):
import pyarrow as pa
import pypaimon.multimodal as pm
table = pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"images", schema=pa.schema([("id", pa.int64()), ("image", pa.large_binary()),
("embedding", pa.list_(pa.float32(), 2))]),
options={"file.format": "parquet", "vector.file.format": "parquet",
"vector-index.search-mode": "full"})
table.add([{"id": 0, "image": b"a", "embedding": [1., 1.]},
{"id": 1, "image": b"b", "embedding": [4., 1.]}])
dataset = table.search([1., 1.]).select(["id", "image"]).limit(1).to_ray()
def resolve(scalar, blobs):
return scalar.append_column("body", pa.array(blobs["image"]))
assert table.map_with_blobs(dataset, ["image"], resolve).take_all() == [{"id": 0, "body": b"a"}]
@pytest.mark.parametrize("projection", [["id"], ["_ROW_ID"]])
@pytest.mark.parametrize("execution", ["local", "ray"])
def test_empty_table_retains_scored_schema(table, ray_cluster, projection, execution):
query = table.search([1., 1.]).select(projection).with_score().order_by_score()
dataset = query.to_ray(execution=execution)
assert dataset.take_all() == []
assert dataset.schema().names == projection + ["_score"]
batch = table.search_vectors([[1., 1.]]).select(projection).with_score().order_by_score()
dataset = batch.to_ray(execution=execution)[0]
assert dataset.take_all() == []
assert dataset.schema().names == projection + ["_score"]