Files
qdrant-client/qdrant_client/uploader/uploader.py
George 9e61a69652 Drop python3.9 (#1110)
* new: remove vectors_count, update http and grpc models

* fix: update inspection cache

* new: add conversions and update interface

* fix: fix some conversions

* fix: fix typo

* fix: fix isinstance

* fix: regen async

* fix: fix update_filter usage, fix isinstance

* tests: collection metadata test

* fix: address backward compatibility in test

* new: update models, add max payload index count and copy vectors

* fix; update _inspection_cache

* new: add read consistency to count points

* Allow uuids in interface (#1085)

* new: direct uuid support

* tests: add uuid tests

* fix: update inspection cache

* new: add collection metadata and tests to local mode (#1089)

* new: add collection metadata and tests to local mode

* fix: regen async client

* new: implement parametrized rrf in local mode (#1087)

* new: implement parametrized rrf in local mode

* refactoring: use a variable for a magic value

* fix: adjust conversion according to AI

* Update filter (#1090)

* new: add missing update_filter, implement it in local mode

* fix: fix type hint, fix update operation, fix rest uploader, add tests

* fix: fix update filter is None case

* fix: mypy was not a good boy

* Text any filter (#1091)

* new: add match text any local mode

* tests: add match text any tests

* new: update models, remove init_from and locks (#1100)

* new: update models, remove init_from and locks

* deprecate: remove init from tests

* deprecate: remove lock tests

* new: convert ascii_folding

* fix: fix type stub

* new: convert acorn

* new: convert shard key with fallback

* new: update grpcio and grpcio tools in generator (#1106)

* new: update grpcio and grpcio tools in generator

* fix: bind grpcio and tools versions to 1.62.0 in generator

* Remove deprecated methods (#1103)

* deprecate: remove old api methods

* deprecate: remove type stub for removed methods

* deprecate: remove old api methods from test_qdrant_client

* deprecate: replace search with query points in test_in_memory

* deprecate: replace search methods in fastembed mixin with query points

* deprecate: replace old api methods in test async qdrant client

* deprecate: replace search with query points in test delete points

* deprecate: replace discover and context with query points in test_discovery

* deprecate: replace recommend_groups with query_points_groups in test_group_recommend

* deprecate: replace search_groups in test_group_search

* deprecate: replace recommend with query points in test_recommendation

* deprecate: replace search with query points in test search

* deprecate: replace context and discover with query points in test sparse discovery

* deprecate: replace search with query points in test sparse idf search

* deprecate: replace recommend with query points in test sparse recommend

* deprecate: replace search with query points in test sparse search

* deprecate: replace missing search request with query request in qdrant_fastembed

* deprecate: replace search with query points in test multivector search queries

* deprecate: replace upload records with upload points in test_updates

* deprecate: remove redundant structs (#1104)

* deprecate: remove redundant structs

* fix: do not use removed conversions in local mode

* fix: remove redundant conversions, simplify types.QueryRequest

* deprecate: replace old style grpc vector conversion to a new one (#1105)

* deprecate: replace old style grpc vector conversion to a new one

* fix: ignore union attr in conversion

* review fixes

---------

Co-authored-by: generall <andrey@vasnetsov.com>

---------

Co-authored-by: generall <andrey@vasnetsov.com>

---------

Co-authored-by: generall <andrey@vasnetsov.com>

* new: deprecate add, query, query_batch in fastembed mixin (#1102)

* new: deprecate add, query, query_batch in fastembed mixin

* 1.16 -> 1.17

---------

Co-authored-by: generall <andrey@vasnetsov.com>

---------

Co-authored-by: generall <andrey@vasnetsov.com>

* new: yet another update

* new: add initial_state to create shard key (#1109)

* new: drop python3.9, replace union and optional with | where possible

* fix: fix missing type hints, regen async

* fix: remove redundant optional

* fix: fix ai comments

* fix: update type hints from merge

* new: update pyproject and lock

* new: replace optional and union with |

* new: remove optional and union from qdrant local

* new: replace union with | in client classes

* fix: replace remaining union, optional, etc, address review comments

* new: adjust numpy versioning

---------

Co-authored-by: generall <andrey@vasnetsov.com>
2025-12-12 17:21:08 +07:00

93 lines
3.2 KiB
Python

from abc import ABC
from itertools import count, islice
from typing import Any, Generator, Iterable
import numpy as np
from qdrant_client.conversions import common_types as types
from qdrant_client.conversions.common_types import Record
from qdrant_client.http.models import ExtendedPointId
from qdrant_client.parallel_processor import Worker
def iter_batch(iterable: Iterable | Generator, size: int) -> Iterable:
"""
>>> list(iter_batch([1,2,3,4,5], 3))
[[1, 2, 3], [4, 5]]
"""
source_iter = iter(iterable)
while source_iter:
b = list(islice(source_iter, size))
if len(b) == 0:
break
yield b
class BaseUploader(Worker, ABC):
@classmethod
def iterate_records_batches(
cls,
records: Iterable[Record | types.PointStruct],
batch_size: int,
) -> Iterable:
record_batches = iter_batch(records, batch_size)
for record_batch in record_batches:
ids_batch, vectors_batch, payload_batch = [], [], []
for record in record_batch:
ids_batch.append(record.id)
vectors_batch.append(record.vector)
payload_batch.append(record.payload)
yield ids_batch, vectors_batch, payload_batch
@classmethod
def iterate_batches(
cls,
vectors: dict[str, types.NumpyArray] | types.NumpyArray | Iterable[types.VectorStruct],
payload: Iterable[dict] | None,
ids: Iterable[ExtendedPointId] | None,
batch_size: int,
) -> Iterable:
if ids is None:
ids_batches: Iterable = (None for _ in count())
else:
ids_batches = iter_batch(ids, batch_size)
if payload is None:
payload_batches: Iterable = (None for _ in count())
else:
payload_batches = iter_batch(payload, batch_size)
if isinstance(vectors, np.ndarray):
vector_batches: Iterable[Any] = cls._vector_batches_from_numpy(vectors, batch_size)
elif isinstance(vectors, dict) and any(
isinstance(value, np.ndarray) for value in vectors.values()
):
vector_batches = cls._vector_batches_from_numpy_named_vectors(vectors, batch_size)
else:
vector_batches = iter_batch(vectors, batch_size)
yield from zip(ids_batches, vector_batches, payload_batches)
@staticmethod
def _vector_batches_from_numpy(vectors: types.NumpyArray, batch_size: int) -> Iterable[float]:
for i in range(0, vectors.shape[0], batch_size):
yield vectors[i : i + batch_size].tolist()
@staticmethod
def _vector_batches_from_numpy_named_vectors(
vectors: dict[str, types.NumpyArray], batch_size: int
) -> Iterable[dict[str, list[float]]]:
assert (
len(set([arr.shape[0] for arr in vectors.values()])) == 1
), "Each named vector should have the same number of vectors"
num_vectors = next(iter(vectors.values())).shape[0]
# Convert dict[str, np.ndarray] to Generator(dict[str, list[float]])
vector_batches = (
{name: vectors[name][i].tolist() for name in vectors.keys()}
for i in range(num_vectors)
)
yield from iter_batch(vector_batches, batch_size)