blob: 2ad37539a169ce6cc38e8c0ddfa6a8ee5950a26e [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.
"""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)