247 lines
10 KiB
Python
247 lines
10 KiB
Python
# Copyright 2025-present the zvec project
|
|
#
|
|
# 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
|
|
#
|
|
# 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 __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
from zvec._zvec import Initialize, _Collection
|
|
|
|
from .model import Collection
|
|
from .model.param import CollectionOption
|
|
from .model.schema import CollectionSchema
|
|
|
|
__all__ = ["create_and_open", "init", "open"]
|
|
|
|
from .typing.enum import LogLevel, LogType
|
|
|
|
|
|
def init(
|
|
*,
|
|
log_type: Optional[LogType] = None,
|
|
log_level: Optional[LogLevel] = None,
|
|
log_dir: Optional[str] = "./logs",
|
|
log_basename: Optional[str] = "zvec.log",
|
|
log_file_size: Optional[int] = 2048,
|
|
log_overdue_days: Optional[int] = 7,
|
|
query_threads: Optional[int] = None,
|
|
optimize_threads: Optional[int] = None,
|
|
invert_to_forward_scan_ratio: Optional[float] = None,
|
|
brute_force_by_keys_ratio: Optional[float] = None,
|
|
fts_brute_force_by_keys_ratio: Optional[float] = None,
|
|
memory_limit_mb: Optional[int] = None,
|
|
jieba_dict_dir: Optional[str] = None,
|
|
) -> None:
|
|
"""Initialize Zvec with configuration options.
|
|
|
|
This function must be called before any other operation.
|
|
It can only be called once — subsequent calls raise a ``RuntimeError``.
|
|
|
|
Parameters set to ``None`` are **omitted** from the configuration and
|
|
fall back to Zvec's internal defaults, which may be derived from the runtime
|
|
environment (e.g., cgroup CPU/memory limits). Explicitly provided values
|
|
always override defaults.
|
|
|
|
Args:
|
|
log_type (Optional[LogType], optional): Logger destination.
|
|
- ``LogType.CONSOLE`` (default if omitted or set to this)
|
|
- ``LogType.FILE``
|
|
- If ``None``, uses internal default (currently ``CONSOLE``).
|
|
log_level (Optional[LogLevel], optional): Minimum log severity.
|
|
Default: ``LogLevel.WARN``.
|
|
Accepted values: ``DEBUG``, ``INFO``, ``WARN``, ``ERROR``, ``FATAL``.
|
|
If ``None``, uses internal default (``WARN``).
|
|
log_dir (Optional[str], optional):
|
|
Directory for log files (only used when ``log_type=FILE``).
|
|
Parent directories are **not** created automatically.
|
|
Default: ``"./logs"``.
|
|
If ``None``, internal default is used.
|
|
log_basename (Optional[str], optional):
|
|
Base name for rotated log files (e.g., ``zvec.log.1``, ``zvec.log.2``).
|
|
Default: ``"zvec.log"``.
|
|
log_file_size (Optional[int], optional):
|
|
Max size per log file in **MB** before rotation.
|
|
Default: ``2048`` MB (2 GB).
|
|
log_overdue_days (Optional[int], optional):
|
|
Days to retain rotated log files before deletion.
|
|
Default: ``7`` days.
|
|
query_threads (Optional[int], optional):
|
|
Number of threads for query execution.
|
|
If ``None`` (default), inferred from available CPU cores (via cgroup).
|
|
Must be ≥ 1 if provided.
|
|
optimize_threads (Optional[int], optional):
|
|
Threads for background tasks (e.g., compaction, indexing).
|
|
If ``None``, defaults to same as ``query_threads`` or CPU count.
|
|
invert_to_forward_scan_ratio (Optional[float], optional):
|
|
Threshold to switch from inverted index to full forward scan.
|
|
Range: [0.0, 1.0]. Higher → more aggressive index skipping.
|
|
Default: ``0.9`` (if omitted).
|
|
brute_force_by_keys_ratio (Optional[float], optional):
|
|
Threshold to use brute-force key lookup over index.
|
|
Lower → prefer index; higher → prefer brute-force.
|
|
Range: [0.0, 1.0]. Default: ``0.1``.
|
|
fts_brute_force_by_keys_ratio (Optional[float], optional):
|
|
Threshold to switch FTS scan from posting-driven to
|
|
candidate-driven (brute-force) when the invert filter is
|
|
highly selective. Independent from ``brute_force_by_keys_ratio``
|
|
because per-candidate FTS cost is higher.
|
|
Range: [0.0, 1.0]. Default: ``0.05``.
|
|
memory_limit_mb (Optional[int], optional):
|
|
Soft memory cap in MB. Zvec may throttle or fail operations
|
|
approaching this limit.
|
|
If ``None``, inferred from cgroup memory limit * 0.8 (e.g., in Docker).
|
|
Must be > 0 if provided.
|
|
jieba_dict_dir (Optional[str], optional):
|
|
Override the default directory containing ``jieba.dict.utf8`` and
|
|
``hmm_model.utf8`` for the jieba FTS tokenizer. When ``None``, the
|
|
value previously registered by ``zvec.set_default_jieba_dict_dir``
|
|
(called automatically on ``import zvec`` to point at the wheel's
|
|
bundled dict) is preserved. JiebaTokenizer also honors the
|
|
``ZVEC_JIEBA_DICT_DIR`` environment variable and per-field
|
|
``FtsIndexParam.extra_params.jieba_dict_dir`` ahead of this value.
|
|
|
|
Raises:
|
|
RuntimeError: If Zvec is already initialized.
|
|
ValueError: On invalid values (e.g., negative thread count, log level out of range).
|
|
TypeError: If a value has incorrect type (e.g., string for ``query_threads``).
|
|
|
|
Note:
|
|
- All ``None`` arguments are **excluded** from the configuration payload,
|
|
allowing the core library to apply environment-aware defaults.
|
|
- This design ensures container-friendliness: in Kubernetes/Docker,
|
|
omitting ``memory_limit_mb`` and thread counts lets Zvec auto-adapt.
|
|
|
|
Examples:
|
|
Initialize with defaults (log to console, auto-detect resources):
|
|
>>> import zvec
|
|
>>> zvec.init()
|
|
|
|
Customize logging to file with rotation:
|
|
>>> zvec.init(
|
|
... log_type=LogType.FILE,
|
|
... log_dir="/var/log/zvec",
|
|
... log_file_size=1024,
|
|
... log_overdue_days=30
|
|
... )
|
|
|
|
Limit resources explicitly:
|
|
>>> zvec.init(
|
|
... memory_limit_mb=2048,
|
|
... query_threads=4,
|
|
... optimize_threads=2
|
|
... )
|
|
|
|
Fine-tune query heuristics:
|
|
>>> zvec.init(
|
|
... invert_to_forward_scan_ratio=0.95,
|
|
... brute_force_by_keys_ratio=0.05
|
|
... )
|
|
"""
|
|
# Build config dict, skipping None values
|
|
config_dict = {}
|
|
if log_type is not None:
|
|
if not isinstance(log_type, LogType):
|
|
raise TypeError("log_type must be LogType")
|
|
config_dict["log_type"] = log_type.name
|
|
if log_level is not None:
|
|
if not isinstance(log_level, LogLevel):
|
|
raise TypeError("log_level must be LogLevel")
|
|
config_dict["log_level"] = log_level.name
|
|
if log_dir is not None:
|
|
config_dict["log_dir"] = log_dir
|
|
if log_basename is not None:
|
|
config_dict["log_basename"] = log_basename
|
|
if log_file_size is not None:
|
|
config_dict["log_file_size"] = log_file_size
|
|
if log_overdue_days is not None:
|
|
config_dict["log_overdue_days"] = log_overdue_days
|
|
if query_threads is not None:
|
|
config_dict["query_threads"] = query_threads
|
|
if optimize_threads is not None:
|
|
config_dict["optimize_threads"] = optimize_threads
|
|
if invert_to_forward_scan_ratio is not None:
|
|
config_dict["invert_to_forward_scan_ratio"] = invert_to_forward_scan_ratio
|
|
if brute_force_by_keys_ratio is not None:
|
|
config_dict["brute_force_by_keys_ratio"] = brute_force_by_keys_ratio
|
|
if fts_brute_force_by_keys_ratio is not None:
|
|
config_dict["fts_brute_force_by_keys_ratio"] = fts_brute_force_by_keys_ratio
|
|
if memory_limit_mb is not None:
|
|
config_dict["memory_limit_mb"] = memory_limit_mb
|
|
if jieba_dict_dir is not None:
|
|
config_dict["jieba_dict_dir"] = jieba_dict_dir
|
|
|
|
Initialize(config_dict)
|
|
|
|
|
|
def create_and_open(
|
|
path: str,
|
|
schema: CollectionSchema,
|
|
option: Optional[CollectionOption] = None,
|
|
) -> Collection:
|
|
"""Create a new collection and open it for use.
|
|
|
|
If a collection already exists at the given path, it may raise an error
|
|
depending on the underlying implementation.
|
|
|
|
Args:
|
|
path (str): Path or name of the collection to create.
|
|
schema (CollectionSchema): Schema defining the structure of the collection.
|
|
option (Optional[CollectionOption]): Configuration options
|
|
for opening the collection. Defaults to a default-constructed
|
|
``CollectionOption()`` if not provided.
|
|
|
|
Returns:
|
|
Collection: An opened collection instance ready for operations.
|
|
|
|
Examples:
|
|
>>> import zvec
|
|
>>> schema = zvec.CollectionSchema(
|
|
... name="my_collection",
|
|
... fields=[zvec.FieldSchema("id", zvec.DataType.INT64, nullable=True)]
|
|
... )
|
|
>>> coll = create_and_open("./my_collection", schema)
|
|
"""
|
|
if not isinstance(path, str):
|
|
raise TypeError("path must be a string")
|
|
if not isinstance(schema, CollectionSchema):
|
|
raise TypeError("schema must be a CollectionSchema")
|
|
|
|
option = option or CollectionOption()
|
|
if not isinstance(option, CollectionOption):
|
|
raise TypeError("option must be a CollectionOption")
|
|
|
|
_collection = _Collection.CreateAndOpen(path, schema._get_object(), option)
|
|
return Collection._from_core(_collection)
|
|
|
|
|
|
def open(path: str, option: CollectionOption = CollectionOption()) -> Collection:
|
|
"""Open an existing collection from disk.
|
|
|
|
The collection must have been previously created with ``create_and_open``.
|
|
|
|
Args:
|
|
path (str): Path or name of the existing collection.
|
|
option (CollectionOption, optional): Configuration options
|
|
for opening the collection. Defaults to a default-constructed
|
|
``CollectionOption()`` if not provided.
|
|
|
|
Returns:
|
|
Collection: An opened collection instance.
|
|
|
|
Examples:
|
|
>>> import zvec
|
|
>>> coll = zvec.open("./my_collection")
|
|
"""
|
|
_collection = _Collection.Open(path, option)
|
|
return Collection._from_core(_collection)
|