| # 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. | |
| """Local REST full-text search delegated to Rust core.""" | |
| from pypaimon.catalog.table_query_auth import reject_search_under_query_auth | |
| from pypaimon.common.options.core_options import GlobalIndexSearchMode | |
| from pypaimon.execution.native.table import (_native_table) | |
| from pypaimon.execution.native.read.builder import (_predicate_to_native) | |
| from pypaimon.execution.native.runtime import (native_runtime_available) | |
| from pypaimon.table.file_store_table import FileStoreTable | |
| from pypaimon.execution.native.read.native_search_plan import _result | |
| from pypaimon.execution.native.write.native_commit import _rest_catalog_supported | |
| def _native_full_text_builder(builder, require_limit=True): | |
| """Qualify the route before execution; Rust owns scan, filtering and ranking.""" | |
| table = builder._table | |
| reject_search_under_query_auth(table) | |
| if (type(table) is not FileStoreTable | |
| or not table.options.native_read_enabled() | |
| or table.options.file_format() != 'parquet' | |
| or not native_runtime_available() | |
| or not _rest_catalog_supported(table)): | |
| return None | |
| primary_key = builder._primary_key_full_text_definition() is not None | |
| if not primary_key and (not table.options.row_tracking_enabled() | |
| or table.is_primary_key_table): | |
| return None | |
| if require_limit and primary_key and table.options.full_text_index_search_mode() != GlobalIndexSearchMode.FAST: | |
| raise NotImplementedError('Primary-key full-text search only supports the FAST full-text index search mode') | |
| if require_limit and (builder._limit is None or builder._limit <= 0): | |
| raise ValueError('Limit must be positive, set via with_limit()') | |
| native = _native_table(table).new_full_text_search_builder() | |
| if builder._field_name is not None and builder._query is not None: | |
| native.with_query(builder._field_name, builder._query) | |
| if builder._limit is not None: | |
| native.with_limit(builder._limit) | |
| try: | |
| if builder._partition_filter is not None: | |
| native.with_partition_filter(_predicate_to_native(builder._partition_filter)) | |
| if builder._filter is not None: | |
| native.with_filter(_predicate_to_native(builder._filter)) | |
| except ValueError: | |
| # Qualify literal support before searching. Some Python literals cannot | |
| # be represented by the strict typed predicate FFI. | |
| return None | |
| return native | |
| def try_native_full_text_search(builder): | |
| native = _native_full_text_builder(builder) | |
| if native is None: | |
| return None | |
| table = builder._table | |
| primary_key = builder._primary_key_full_text_definition() is not None | |
| # Once execution starts, propagate errors instead of running a second search. | |
| result = native.execute() | |
| return _result(table, result, primary_key) |