blob: 5c087ee5bc9deffab22e60c74d4b9a2b162dcab8 [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.
import ctypes
from dataclasses import dataclass
from ctypes import c_float, c_int64, c_size_t, c_uint8, c_void_p
from ._ffi import (
PREAD_FN,
PaimonFtindexInputFile,
PaimonFtindexReadMetrics,
check_ptr,
check_status,
lib,
)
@dataclass(frozen=True)
class FullTextReadMetrics:
pread_calls: int
pread_ranges: int
pread_bytes: int
cache_hits: int
cache_misses: int
cache_evictions: int
cached_blocks: int
class FullTextIndexReader:
def __init__(self, input_):
self._closed = False
self._input = input_
@PREAD_FN
def pread_fn(ctx, pos, buf, length):
try:
data = input_.pread(int(pos), int(length))
if len(data) != length:
return -1
ctypes.memmove(buf, data, length)
return 0
except Exception:
return -1
self._pread_fn = pread_fn
native_input = PaimonFtindexInputFile(c_void_p(0), pread_fn)
self._ptr = check_ptr(lib.paimon_ftindex_reader_open(native_input))
def search(self, query, limit=10, filter_bytes=None):
if self._closed:
raise RuntimeError("FullTextIndexReader is closed")
query = str(query)
capacity = int(limit)
row_ids = (c_int64 * capacity)()
scores = (c_float * capacity)()
result_len = c_size_t()
if filter_bytes is None:
status = lib.paimon_ftindex_reader_search(
self._ptr,
query.encode("utf-8"),
capacity,
row_ids,
scores,
capacity,
ctypes.byref(result_len),
)
else:
filter_bytes = bytes(filter_bytes)
filter_buf = (c_uint8 * len(filter_bytes)).from_buffer_copy(filter_bytes)
status = lib.paimon_ftindex_reader_search_with_roaring_filter(
self._ptr,
query.encode("utf-8"),
capacity,
filter_buf,
len(filter_bytes),
row_ids,
scores,
capacity,
ctypes.byref(result_len),
)
check_status(status)
size = result_len.value
return list(row_ids[:size]), list(scores[:size])
def prewarm(self):
if self._closed:
raise RuntimeError("FullTextIndexReader is closed")
check_status(lib.paimon_ftindex_reader_prewarm(self._ptr))
def read_metrics(self):
if self._closed:
raise RuntimeError("FullTextIndexReader is closed")
native = PaimonFtindexReadMetrics()
check_status(
lib.paimon_ftindex_reader_read_metrics(self._ptr, ctypes.byref(native))
)
return FullTextReadMetrics(
pread_calls=native.pread_calls,
pread_ranges=native.pread_ranges,
pread_bytes=native.pread_bytes,
cache_hits=native.cache_hits,
cache_misses=native.cache_misses,
cache_evictions=native.cache_evictions,
cached_blocks=native.cached_blocks,
)
def close(self):
if not self._closed:
self._closed = True
if self._ptr:
lib.paimon_ftindex_reader_free(self._ptr)
self._ptr = None
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
self.close()
def __del__(self):
self.close()