From bb8c442169e388b37954effb1d3ad7da381daf64 Mon Sep 17 00:00:00 2001 From: Andrey Vasnetsov Date: Thu, 5 Oct 2023 03:38:19 +0200 Subject: [PATCH] Recommendation api update + local mode (#314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP: recommendation api update * implement local new reco + add new tests * refactor calculate_best_scores, normalize score assertion * fix raw vectors in new recommend * fix mypy errors, add new reco on group recommend * fix more lints from ci * fix pyright lint * formattng * Add descriptions to enums * no mutable default argument * edit score comparison precision based on magnitude * Address review comments * remove custom message for type ignore * add coverage test * improve coverage test a little * add more fixtures for conversion test --------- Co-authored-by: Luis Cossío --- .gitignore | 2 + qdrant_client/client_base.py | 12 +- qdrant_client/conversions/common_types.py | 3 + qdrant_client/conversions/conversion.py | 90 +++++++- qdrant_client/grpc/collections_pb2.py | 132 +++++------ qdrant_client/grpc/points_pb2.py | 218 +++++++++--------- qdrant_client/grpc/points_service_pb2_grpc.py | 2 +- qdrant_client/http/models/models.py | 97 +++++++- qdrant_client/local/distances.py | 55 ++++- qdrant_client/local/local_collection.py | 193 ++++++++++++---- qdrant_client/local/qdrant_local.py | 13 +- qdrant_client/proto/collections.proto | 2 + qdrant_client/proto/points.proto | 28 ++- qdrant_client/proto/points_service.proto | 2 +- qdrant_client/qdrant_client.py | 52 +++-- qdrant_client/qdrant_remote.py | 91 ++++---- tests/congruence_tests/test_common.py | 7 +- .../congruence_tests/test_group_recommend.py | 17 ++ tests/congruence_tests/test_recommendation.py | 72 +++++- tests/conversions/fixtures.py | 20 ++ .../conversions/test_validate_conversions.py | 6 +- tests/coverage-test.sh | 2 +- tests/test_qdrant_client.py | 10 +- tests/type_stub.py | 1 + 24 files changed, 809 insertions(+), 318 deletions(-) diff --git a/.gitignore b/.gitignore index 9e9fea5d..1e96a188 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ venv __pycache__ .pytest_cache .idea +.vscode +.devcontainer .coverage htmlcov *.iml diff --git a/qdrant_client/client_base.py b/qdrant_client/client_base.py index ef52b61a..2a51d6fe 100644 --- a/qdrant_client/client_base.py +++ b/qdrant_client/client_base.py @@ -69,8 +69,8 @@ def recommend_batch( def recommend( self, collection_name: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, query_filter: Optional[types.Filter] = None, search_params: Optional[types.SearchParams] = None, limit: int = 10, @@ -79,7 +79,8 @@ def recommend( with_vectors: Union[bool, List[str]] = False, score_threshold: Optional[float] = None, using: Optional[str] = None, - lookup_from: Optional[models.LookupLocation] = None, + lookup_from: Optional[types.LookupLocation] = None, + strategy: Optional[types.RecommendStrategy] = None, **kwargs: Any, ) -> List[types.ScoredPoint]: raise NotImplementedError() @@ -88,8 +89,8 @@ def recommend_groups( self, collection_name: str, group_by: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, query_filter: Optional[models.Filter] = None, search_params: Optional[models.SearchParams] = None, limit: int = 10, @@ -100,6 +101,7 @@ def recommend_groups( using: Optional[str] = None, lookup_from: Optional[models.LookupLocation] = None, with_lookup: Optional[types.WithLookupInterface] = None, + strategy: Optional[types.RecommendStrategy] = None, **kwargs: Any, ) -> types.GroupsResult: raise NotImplementedError() diff --git a/qdrant_client/conversions/common_types.py b/qdrant_client/conversions/common_types.py index ca91c30f..6d650c8c 100644 --- a/qdrant_client/conversions/common_types.py +++ b/qdrant_client/conversions/common_types.py @@ -58,6 +58,8 @@ def get_args_subscribed(tp: type): # type: ignore List[PointId], rest.Filter, grpc.Filter, rest.PointsSelector, grpc.PointsSelector ] LookupLocation = Union[rest.LookupLocation, grpc.LookupLocation] +RecommendStrategy: TypeAlias = rest.RecommendStrategy +RecommendExample: TypeAlias = rest.RecommendExample AliasOperations = Union[ rest.CreateAliasOperation, @@ -84,6 +86,7 @@ def get_args_subscribed(tp: type): # type: ignore InitFrom: TypeAlias = Union[rest.InitFrom, str] UpdateOperation: TypeAlias = rest.UpdateOperation + SearchRequest = Union[rest.SearchRequest, grpc.SearchPoints] RecommendRequest = Union[rest.RecommendRequest, grpc.RecommendPoints] diff --git a/qdrant_client/conversions/conversion.py b/qdrant_client/conversions/conversion.py index 698f6427..e71d0b0e 100644 --- a/qdrant_client/conversions/conversion.py +++ b/qdrant_client/conversions/conversion.py @@ -1,10 +1,11 @@ from datetime import datetime -from typing import Any, Dict, List, Optional, Tuple, get_args +from typing import Any, Dict, List, Optional, Sequence, Tuple, get_args from google.protobuf.json_format import MessageToDict from google.protobuf.timestamp_pb2 import Timestamp from qdrant_client._pydantic_compat import construct +from qdrant_client.conversions.common_types import get_args_subscribed try: from google.protobuf.pyext._message import MessageMapContainer # type: ignore @@ -518,6 +519,9 @@ def convert_collection_params(cls, model: grpc.CollectionParams) -> rest.Collect replication_factor=model.replication_factor if model.HasField("replication_factor") else None, + read_fan_out_factor=model.read_fan_out_factor + if model.HasField("read_fan_out_factor") + else None, write_consistency_factor=model.write_consistency_factor if model.HasField("write_consistency_factor") else None, @@ -741,9 +745,15 @@ def convert_search_points(cls, model: grpc.SearchPoints) -> rest.SearchRequest: @classmethod def convert_recommend_points(cls, model: grpc.RecommendPoints) -> rest.RecommendRequest: + positive_ids = [cls.convert_point_id(point_id) for point_id in model.positive] + negative_ids = [cls.convert_point_id(point_id) for point_id in model.negative] + + positive_vectors = [cls.convert_vector(vector) for vector in model.positive_vectors] + negative_vectors = [cls.convert_vector(vector) for vector in model.negative_vectors] + return rest.RecommendRequest( - positive=[cls.convert_point_id(point_id) for point_id in model.positive], - negative=[cls.convert_point_id(point_id) for point_id in model.negative], + positive=positive_ids + positive_vectors, + negative=negative_ids + negative_vectors, filter=cls.convert_filter(model.filter) if model.HasField("filter") else None, limit=model.limit, with_payload=cls.convert_with_payload_interface(model.with_payload) @@ -759,6 +769,9 @@ def convert_recommend_points(cls, model: grpc.RecommendPoints) -> rest.Recommend lookup_from=cls.convert_lookup_location(model.lookup_from) if model.HasField("lookup_from") else None, + strategy=cls.convert_recommend_strategy(model.strategy) + if model.HasField("strategy") + else None, ) @classmethod @@ -794,6 +807,9 @@ def convert_collection_params_diff( write_consistency_factor=model.write_consistency_factor if model.HasField("write_consistency_factor") else None, + read_fan_out_factor=model.read_fan_out_factor + if model.HasField("read_fan_out_factor") + else None, on_disk_payload=model.on_disk_payload if model.HasField("on_disk_payload") else None, ) @@ -1094,6 +1110,14 @@ def convert_init_from(cls, model: str) -> rest.InitFrom: return rest.InitFrom(collection=model) raise ValueError(f"Invalid InitFrom model: {model}") + @classmethod + def convert_recommend_strategy(cls, model: grpc.RecommendStrategy) -> rest.RecommendStrategy: + if model == grpc.RecommendStrategy.AverageVector: + return rest.RecommendStrategy.AVERAGE_VECTOR + if model == grpc.RecommendStrategy.BestScore: + return rest.RecommendStrategy.BEST_SCORE + raise ValueError(f"invalid RecommendStrategy model: {model}") + # ---------------------------------------- # @@ -1426,6 +1450,7 @@ def convert_collection_params(cls, model: rest.CollectionParams) -> grpc.Collect on_disk_payload=model.on_disk_payload or False, write_consistency_factor=model.write_consistency_factor, replication_factor=model.replication_factor, + read_fan_out_factor=model.read_fan_out_factor, ) @classmethod @@ -1531,6 +1556,40 @@ def convert_alias_description(cls, model: rest.AliasDescription) -> grpc.AliasDe collection_name=model.collection_name, ) + @classmethod + def convert_recommend_examples_to_ids( + cls, examples: Sequence[rest.RecommendExample] + ) -> List[grpc.PointId]: + ids: List[grpc.PointId] = [] + for example in examples: + if isinstance(example, get_args_subscribed(rest.ExtendedPointId)): + id_ = cls.convert_extended_point_id(example) + elif isinstance(example, grpc.PointId): + id_ = example + else: + continue + + ids.append(id_) + + return ids + + @classmethod + def convert_recommend_examples_to_vectors( + cls, examples: Sequence[rest.RecommendExample] + ) -> List[grpc.Vector]: + vectors: List[grpc.Vector] = [] + for example in examples: + if isinstance(example, grpc.Vector): + vector = example + elif isinstance(example, list): + vector = grpc.Vector(data=example) + else: + continue + + vectors.append(vector) + + return vectors + @classmethod def convert_extended_point_id(cls, model: rest.ExtendedPointId) -> grpc.PointId: if isinstance(model, int): @@ -1735,10 +1794,16 @@ def convert_search_points( def convert_recommend_request( cls, model: rest.RecommendRequest, collection_name: str ) -> grpc.RecommendPoints: + positive_ids = cls.convert_recommend_examples_to_ids(model.positive) + negative_ids = cls.convert_recommend_examples_to_ids(model.negative) + + positive_vectors = cls.convert_recommend_examples_to_vectors(model.positive) + negative_vectors = cls.convert_recommend_examples_to_vectors(model.negative) + return grpc.RecommendPoints( collection_name=collection_name, - positive=[cls.convert_extended_point_id(point_id) for point_id in model.positive], - negative=[cls.convert_extended_point_id(point_id) for point_id in model.negative], + positive=positive_ids, + negative=negative_ids, filter=cls.convert_filter(model.filter) if model.filter is not None else None, limit=model.limit, with_payload=cls.convert_with_payload_interface(model.with_payload) @@ -1754,6 +1819,11 @@ def convert_recommend_request( lookup_from=cls.convert_lookup_location(model.lookup_from) if model.lookup_from is not None else None, + strategy=cls.convert_recommend_strategy(model.strategy) + if model.strategy is not None + else None, + positive_vectors=positive_vectors, + negative_vectors=negative_vectors, ) @classmethod @@ -1794,6 +1864,7 @@ def convert_collection_params_diff( replication_factor=model.replication_factor, write_consistency_factor=model.write_consistency_factor, on_disk_payload=model.on_disk_payload, + read_fan_out_factor=model.read_fan_out_factor, ) @classmethod @@ -2136,3 +2207,12 @@ def convert_init_from(cls, model: rest.InitFrom) -> str: return model.collection else: raise ValueError(f"invalid InitFrom model: {model}") + + @classmethod + def convert_recommend_strategy(cls, model: rest.RecommendStrategy) -> grpc.RecommendStrategy: + if model == rest.RecommendStrategy.AVERAGE_VECTOR: + return grpc.RecommendStrategy.AverageVector + elif model == rest.RecommendStrategy.BEST_SCORE: + return grpc.RecommendStrategy.BestScore + else: + raise ValueError(f"invalid RecommendStrategy model: {model}") diff --git a/qdrant_client/grpc/collections_pb2.py b/qdrant_client/grpc/collections_pb2.py index 429206b1..35a5914d 100644 --- a/qdrant_client/grpc/collections_pb2.py +++ b/qdrant_client/grpc/collections_pb2.py @@ -15,7 +15,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x63ollections.proto\x12\x06qdrant\"\xfa\x01\n\x0cVectorParams\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\"\n\x08\x64istance\x18\x02 \x01(\x0e\x32\x10.qdrant.Distance\x12\x30\n\x0bhnsw_config\x18\x03 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x00\x88\x01\x01\x12<\n\x13quantization_config\x18\x04 \x01(\x0b\x32\x1a.qdrant.QuantizationConfigH\x01\x88\x01\x01\x12\x14\n\x07on_disk\x18\x05 \x01(\x08H\x02\x88\x01\x01\x42\x0e\n\x0c_hnsw_configB\x16\n\x14_quantization_configB\n\n\x08_on_disk\"\xd0\x01\n\x10VectorParamsDiff\x12\x30\n\x0bhnsw_config\x18\x01 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x00\x88\x01\x01\x12@\n\x13quantization_config\x18\x02 \x01(\x0b\x32\x1e.qdrant.QuantizationConfigDiffH\x01\x88\x01\x01\x12\x14\n\x07on_disk\x18\x03 \x01(\x08H\x02\x88\x01\x01\x42\x0e\n\x0c_hnsw_configB\x16\n\x14_quantization_configB\n\n\x08_on_disk\"\x82\x01\n\x0fVectorParamsMap\x12-\n\x03map\x18\x01 \x03(\x0b\x32 .qdrant.VectorParamsMap.MapEntry\x1a@\n\x08MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.qdrant.VectorParams:\x02\x38\x01\"\x8e\x01\n\x13VectorParamsDiffMap\x12\x31\n\x03map\x18\x01 \x03(\x0b\x32$.qdrant.VectorParamsDiffMap.MapEntry\x1a\x44\n\x08MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x18.qdrant.VectorParamsDiff:\x02\x38\x01\"p\n\rVectorsConfig\x12&\n\x06params\x18\x01 \x01(\x0b\x32\x14.qdrant.VectorParamsH\x00\x12-\n\nparams_map\x18\x02 \x01(\x0b\x32\x17.qdrant.VectorParamsMapH\x00\x42\x08\n\x06\x63onfig\"|\n\x11VectorsConfigDiff\x12*\n\x06params\x18\x01 \x01(\x0b\x32\x18.qdrant.VectorParamsDiffH\x00\x12\x31\n\nparams_map\x18\x02 \x01(\x0b\x32\x1b.qdrant.VectorParamsDiffMapH\x00\x42\x08\n\x06\x63onfig\"3\n\x18GetCollectionInfoRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\"\x18\n\x16ListCollectionsRequest\"%\n\x15\x43ollectionDescription\x12\x0c\n\x04name\x18\x01 \x01(\t\"Q\n\x19GetCollectionInfoResponse\x12&\n\x06result\x18\x01 \x01(\x0b\x32\x16.qdrant.CollectionInfo\x12\x0c\n\x04time\x18\x02 \x01(\x01\"[\n\x17ListCollectionsResponse\x12\x32\n\x0b\x63ollections\x18\x01 \x03(\x0b\x32\x1d.qdrant.CollectionDescription\x12\x0c\n\x04time\x18\x02 \x01(\x01\",\n\x0fOptimizerStatus\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\x90\x02\n\x0eHnswConfigDiff\x12\x0e\n\x01m\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x19\n\x0c\x65\x66_construct\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12 \n\x13\x66ull_scan_threshold\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12!\n\x14max_indexing_threads\x18\x04 \x01(\x04H\x03\x88\x01\x01\x12\x14\n\x07on_disk\x18\x05 \x01(\x08H\x04\x88\x01\x01\x12\x16\n\tpayload_m\x18\x06 \x01(\x04H\x05\x88\x01\x01\x42\x04\n\x02_mB\x0f\n\r_ef_constructB\x16\n\x14_full_scan_thresholdB\x17\n\x15_max_indexing_threadsB\n\n\x08_on_diskB\x0c\n\n_payload_m\"y\n\rWalConfigDiff\x12\x1c\n\x0fwal_capacity_mb\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1f\n\x12wal_segments_ahead\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x12\n\x10_wal_capacity_mbB\x15\n\x13_wal_segments_ahead\"\xec\x03\n\x14OptimizersConfigDiff\x12\x1e\n\x11\x64\x65leted_threshold\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12%\n\x18vacuum_min_vector_number\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12#\n\x16\x64\x65\x66\x61ult_segment_number\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x10max_segment_size\x18\x04 \x01(\x04H\x03\x88\x01\x01\x12\x1d\n\x10memmap_threshold\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x1f\n\x12indexing_threshold\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x1f\n\x12\x66lush_interval_sec\x18\x07 \x01(\x04H\x06\x88\x01\x01\x12%\n\x18max_optimization_threads\x18\x08 \x01(\x04H\x07\x88\x01\x01\x42\x14\n\x12_deleted_thresholdB\x1b\n\x19_vacuum_min_vector_numberB\x19\n\x17_default_segment_numberB\x13\n\x11_max_segment_sizeB\x13\n\x11_memmap_thresholdB\x15\n\x13_indexing_thresholdB\x15\n\x13_flush_interval_secB\x1b\n\x19_max_optimization_threads\"\x88\x01\n\x12ScalarQuantization\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.qdrant.QuantizationType\x12\x15\n\x08quantile\x18\x02 \x01(\x02H\x00\x88\x01\x01\x12\x17\n\nalways_ram\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x0b\n\t_quantileB\r\n\x0b_always_ram\"l\n\x13ProductQuantization\x12-\n\x0b\x63ompression\x18\x01 \x01(\x0e\x32\x18.qdrant.CompressionRatio\x12\x17\n\nalways_ram\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_always_ram\"<\n\x12\x42inaryQuantization\x12\x17\n\nalways_ram\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_always_ram\"\xb0\x01\n\x12QuantizationConfig\x12,\n\x06scalar\x18\x01 \x01(\x0b\x32\x1a.qdrant.ScalarQuantizationH\x00\x12.\n\x07product\x18\x02 \x01(\x0b\x32\x1b.qdrant.ProductQuantizationH\x00\x12,\n\x06\x62inary\x18\x03 \x01(\x0b\x32\x1a.qdrant.BinaryQuantizationH\x00\x42\x0e\n\x0cquantization\"\n\n\x08\x44isabled\"\xda\x01\n\x16QuantizationConfigDiff\x12,\n\x06scalar\x18\x01 \x01(\x0b\x32\x1a.qdrant.ScalarQuantizationH\x00\x12.\n\x07product\x18\x02 \x01(\x0b\x32\x1b.qdrant.ProductQuantizationH\x00\x12$\n\x08\x64isabled\x18\x03 \x01(\x0b\x32\x10.qdrant.DisabledH\x00\x12,\n\x06\x62inary\x18\x04 \x01(\x0b\x32\x1a.qdrant.BinaryQuantizationH\x00\x42\x0e\n\x0cquantization\"\xe1\x05\n\x10\x43reateCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x30\n\x0bhnsw_config\x18\x04 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x00\x88\x01\x01\x12.\n\nwal_config\x18\x05 \x01(\x0b\x32\x15.qdrant.WalConfigDiffH\x01\x88\x01\x01\x12<\n\x11optimizers_config\x18\x06 \x01(\x0b\x32\x1c.qdrant.OptimizersConfigDiffH\x02\x88\x01\x01\x12\x19\n\x0cshard_number\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fon_disk_payload\x18\x08 \x01(\x08H\x04\x88\x01\x01\x12\x14\n\x07timeout\x18\t \x01(\x04H\x05\x88\x01\x01\x12\x32\n\x0evectors_config\x18\n \x01(\x0b\x32\x15.qdrant.VectorsConfigH\x06\x88\x01\x01\x12\x1f\n\x12replication_factor\x18\x0b \x01(\rH\x07\x88\x01\x01\x12%\n\x18write_consistency_factor\x18\x0c \x01(\rH\x08\x88\x01\x01\x12!\n\x14init_from_collection\x18\r \x01(\tH\t\x88\x01\x01\x12<\n\x13quantization_config\x18\x0e \x01(\x0b\x32\x1a.qdrant.QuantizationConfigH\n\x88\x01\x01\x42\x0e\n\x0c_hnsw_configB\r\n\x0b_wal_configB\x14\n\x12_optimizers_configB\x0f\n\r_shard_numberB\x12\n\x10_on_disk_payloadB\n\n\x08_timeoutB\x11\n\x0f_vectors_configB\x15\n\x13_replication_factorB\x1b\n\x19_write_consistency_factorB\x17\n\x15_init_from_collectionB\x16\n\x14_quantization_configJ\x04\x08\x02\x10\x03J\x04\x08\x03\x10\x04\"\xc6\x03\n\x10UpdateCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12<\n\x11optimizers_config\x18\x02 \x01(\x0b\x32\x1c.qdrant.OptimizersConfigDiffH\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x31\n\x06params\x18\x04 \x01(\x0b\x32\x1c.qdrant.CollectionParamsDiffH\x02\x88\x01\x01\x12\x30\n\x0bhnsw_config\x18\x05 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x03\x88\x01\x01\x12\x36\n\x0evectors_config\x18\x06 \x01(\x0b\x32\x19.qdrant.VectorsConfigDiffH\x04\x88\x01\x01\x12@\n\x13quantization_config\x18\x07 \x01(\x0b\x32\x1e.qdrant.QuantizationConfigDiffH\x05\x88\x01\x01\x42\x14\n\x12_optimizers_configB\n\n\x08_timeoutB\t\n\x07_paramsB\x0e\n\x0c_hnsw_configB\x11\n\x0f_vectors_configB\x16\n\x14_quantization_config\"M\n\x10\x44\x65leteCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_timeout\";\n\x1b\x43ollectionOperationResponse\x12\x0e\n\x06result\x18\x01 \x01(\x08\x12\x0c\n\x04time\x18\x02 \x01(\x01\"\x90\x02\n\x10\x43ollectionParams\x12\x14\n\x0cshard_number\x18\x03 \x01(\r\x12\x17\n\x0fon_disk_payload\x18\x04 \x01(\x08\x12\x32\n\x0evectors_config\x18\x05 \x01(\x0b\x32\x15.qdrant.VectorsConfigH\x00\x88\x01\x01\x12\x1f\n\x12replication_factor\x18\x06 \x01(\rH\x01\x88\x01\x01\x12%\n\x18write_consistency_factor\x18\x07 \x01(\rH\x02\x88\x01\x01\x42\x11\n\x0f_vectors_configB\x15\n\x13_replication_factorB\x1b\n\x19_write_consistency_factorJ\x04\x08\x01\x10\x02J\x04\x08\x02\x10\x03\"\xc4\x01\n\x14\x43ollectionParamsDiff\x12\x1f\n\x12replication_factor\x18\x01 \x01(\rH\x00\x88\x01\x01\x12%\n\x18write_consistency_factor\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x0fon_disk_payload\x18\x03 \x01(\x08H\x02\x88\x01\x01\x42\x15\n\x13_replication_factorB\x1b\n\x19_write_consistency_factorB\x12\n\x10_on_disk_payload\"\xa2\x02\n\x10\x43ollectionConfig\x12(\n\x06params\x18\x01 \x01(\x0b\x32\x18.qdrant.CollectionParams\x12+\n\x0bhnsw_config\x18\x02 \x01(\x0b\x32\x16.qdrant.HnswConfigDiff\x12\x36\n\x10optimizer_config\x18\x03 \x01(\x0b\x32\x1c.qdrant.OptimizersConfigDiff\x12)\n\nwal_config\x18\x04 \x01(\x0b\x32\x15.qdrant.WalConfigDiff\x12<\n\x13quantization_config\x18\x05 \x01(\x0b\x32\x1a.qdrant.QuantizationConfigH\x00\x88\x01\x01\x42\x16\n\x14_quantization_config\"\xbd\x01\n\x0fTextIndexParams\x12(\n\ttokenizer\x18\x01 \x01(\x0e\x32\x15.qdrant.TokenizerType\x12\x16\n\tlowercase\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rmin_token_len\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x1a\n\rmax_token_len\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\x0c\n\n_lowercaseB\x10\n\x0e_min_token_lenB\x10\n\x0e_max_token_len\"Z\n\x12PayloadIndexParams\x12\x34\n\x11text_index_params\x18\x01 \x01(\x0b\x32\x17.qdrant.TextIndexParamsH\x00\x42\x0e\n\x0cindex_params\"\x9d\x01\n\x11PayloadSchemaInfo\x12,\n\tdata_type\x18\x01 \x01(\x0e\x32\x19.qdrant.PayloadSchemaType\x12/\n\x06params\x18\x02 \x01(\x0b\x32\x1a.qdrant.PayloadIndexParamsH\x00\x88\x01\x01\x12\x13\n\x06points\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\t\n\x07_paramsB\t\n\x07_points\"\xba\x03\n\x0e\x43ollectionInfo\x12(\n\x06status\x18\x01 \x01(\x0e\x32\x18.qdrant.CollectionStatus\x12\x31\n\x10optimizer_status\x18\x02 \x01(\x0b\x32\x17.qdrant.OptimizerStatus\x12\x15\n\rvectors_count\x18\x03 \x01(\x04\x12\x16\n\x0esegments_count\x18\x04 \x01(\x04\x12(\n\x06\x63onfig\x18\x07 \x01(\x0b\x32\x18.qdrant.CollectionConfig\x12\x41\n\x0epayload_schema\x18\x08 \x03(\x0b\x32).qdrant.CollectionInfo.PayloadSchemaEntry\x12\x14\n\x0cpoints_count\x18\t \x01(\x04\x12\"\n\x15indexed_vectors_count\x18\n \x01(\x04H\x00\x88\x01\x01\x1aO\n\x12PayloadSchemaEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.qdrant.PayloadSchemaInfo:\x02\x38\x01\x42\x18\n\x16_indexed_vectors_countJ\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07\"[\n\rChangeAliases\x12(\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32\x17.qdrant.AliasOperations\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_timeout\"\xa2\x01\n\x0f\x41liasOperations\x12+\n\x0c\x63reate_alias\x18\x01 \x01(\x0b\x32\x13.qdrant.CreateAliasH\x00\x12+\n\x0crename_alias\x18\x02 \x01(\x0b\x32\x13.qdrant.RenameAliasH\x00\x12+\n\x0c\x64\x65lete_alias\x18\x03 \x01(\x0b\x32\x13.qdrant.DeleteAliasH\x00\x42\x08\n\x06\x61\x63tion\":\n\x0b\x43reateAlias\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x12\n\nalias_name\x18\x02 \x01(\t\"=\n\x0bRenameAlias\x12\x16\n\x0eold_alias_name\x18\x01 \x01(\t\x12\x16\n\x0enew_alias_name\x18\x02 \x01(\t\"!\n\x0b\x44\x65leteAlias\x12\x12\n\nalias_name\x18\x01 \x01(\t\"\x14\n\x12ListAliasesRequest\"7\n\x1cListCollectionAliasesRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\"?\n\x10\x41liasDescription\x12\x12\n\nalias_name\x18\x01 \x01(\t\x12\x17\n\x0f\x63ollection_name\x18\x02 \x01(\t\"N\n\x13ListAliasesResponse\x12)\n\x07\x61liases\x18\x01 \x03(\x0b\x32\x18.qdrant.AliasDescription\x12\x0c\n\x04time\x18\x02 \x01(\x01\"7\n\x1c\x43ollectionClusterInfoRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\"]\n\x0eLocalShardInfo\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x14\n\x0cpoints_count\x18\x02 \x01(\x04\x12#\n\x05state\x18\x03 \x01(\x0e\x32\x14.qdrant.ReplicaState\"Y\n\x0fRemoteShardInfo\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x0f\n\x07peer_id\x18\x02 \x01(\x04\x12#\n\x05state\x18\x03 \x01(\x0e\x32\x14.qdrant.ReplicaState\"M\n\x11ShardTransferInfo\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x0c\n\x04\x66rom\x18\x02 \x01(\x04\x12\n\n\x02to\x18\x03 \x01(\x04\x12\x0c\n\x04sync\x18\x04 \x01(\x08\"\xd7\x01\n\x1d\x43ollectionClusterInfoResponse\x12\x0f\n\x07peer_id\x18\x01 \x01(\x04\x12\x13\n\x0bshard_count\x18\x02 \x01(\x04\x12,\n\x0clocal_shards\x18\x03 \x03(\x0b\x32\x16.qdrant.LocalShardInfo\x12.\n\rremote_shards\x18\x04 \x03(\x0b\x32\x17.qdrant.RemoteShardInfo\x12\x32\n\x0fshard_transfers\x18\x05 \x03(\x0b\x32\x19.qdrant.ShardTransferInfo\"G\n\tMoveShard\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x14\n\x0c\x66rom_peer_id\x18\x02 \x01(\x04\x12\x12\n\nto_peer_id\x18\x03 \x01(\x04\",\n\x07Replica\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x0f\n\x07peer_id\x18\x02 \x01(\x04\"\x9a\x02\n#UpdateCollectionClusterSetupRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\'\n\nmove_shard\x18\x02 \x01(\x0b\x32\x11.qdrant.MoveShardH\x00\x12,\n\x0freplicate_shard\x18\x03 \x01(\x0b\x32\x11.qdrant.MoveShardH\x00\x12+\n\x0e\x61\x62ort_transfer\x18\x04 \x01(\x0b\x32\x11.qdrant.MoveShardH\x00\x12\'\n\x0c\x64rop_replica\x18\x05 \x01(\x0b\x32\x0f.qdrant.ReplicaH\x00\x12\x14\n\x07timeout\x18\x06 \x01(\x04H\x01\x88\x01\x01\x42\x0b\n\toperationB\n\n\x08_timeout\"6\n$UpdateCollectionClusterSetupResponse\x12\x0e\n\x06result\x18\x01 \x01(\x08*@\n\x08\x44istance\x12\x13\n\x0fUnknownDistance\x10\x00\x12\n\n\x06\x43osine\x10\x01\x12\n\n\x06\x45uclid\x10\x02\x12\x07\n\x03\x44ot\x10\x03*O\n\x10\x43ollectionStatus\x12\x1b\n\x17UnknownCollectionStatus\x10\x00\x12\t\n\x05Green\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\x07\n\x03Red\x10\x03*f\n\x11PayloadSchemaType\x12\x0f\n\x0bUnknownType\x10\x00\x12\x0b\n\x07Keyword\x10\x01\x12\x0b\n\x07Integer\x10\x02\x12\t\n\x05\x46loat\x10\x03\x12\x07\n\x03Geo\x10\x04\x12\x08\n\x04Text\x10\x05\x12\x08\n\x04\x42ool\x10\x06*5\n\x10QuantizationType\x12\x17\n\x13UnknownQuantization\x10\x00\x12\x08\n\x04Int8\x10\x01*=\n\x10\x43ompressionRatio\x12\x06\n\x02x4\x10\x00\x12\x06\n\x02x8\x10\x01\x12\x07\n\x03x16\x10\x02\x12\x07\n\x03x32\x10\x03\x12\x07\n\x03x64\x10\x04*T\n\rTokenizerType\x12\x0b\n\x07Unknown\x10\x00\x12\n\n\x06Prefix\x10\x01\x12\x0e\n\nWhitespace\x10\x02\x12\x08\n\x04Word\x10\x03\x12\x10\n\x0cMultilingual\x10\x04*Q\n\x0cReplicaState\x12\n\n\x06\x41\x63tive\x10\x00\x12\x08\n\x04\x44\x65\x61\x64\x10\x01\x12\x0b\n\x07Partial\x10\x02\x12\x10\n\x0cInitializing\x10\x03\x12\x0c\n\x08Listener\x10\x04\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x63ollections.proto\x12\x06qdrant\"\xfa\x01\n\x0cVectorParams\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\"\n\x08\x64istance\x18\x02 \x01(\x0e\x32\x10.qdrant.Distance\x12\x30\n\x0bhnsw_config\x18\x03 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x00\x88\x01\x01\x12<\n\x13quantization_config\x18\x04 \x01(\x0b\x32\x1a.qdrant.QuantizationConfigH\x01\x88\x01\x01\x12\x14\n\x07on_disk\x18\x05 \x01(\x08H\x02\x88\x01\x01\x42\x0e\n\x0c_hnsw_configB\x16\n\x14_quantization_configB\n\n\x08_on_disk\"\xd0\x01\n\x10VectorParamsDiff\x12\x30\n\x0bhnsw_config\x18\x01 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x00\x88\x01\x01\x12@\n\x13quantization_config\x18\x02 \x01(\x0b\x32\x1e.qdrant.QuantizationConfigDiffH\x01\x88\x01\x01\x12\x14\n\x07on_disk\x18\x03 \x01(\x08H\x02\x88\x01\x01\x42\x0e\n\x0c_hnsw_configB\x16\n\x14_quantization_configB\n\n\x08_on_disk\"\x82\x01\n\x0fVectorParamsMap\x12-\n\x03map\x18\x01 \x03(\x0b\x32 .qdrant.VectorParamsMap.MapEntry\x1a@\n\x08MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.qdrant.VectorParams:\x02\x38\x01\"\x8e\x01\n\x13VectorParamsDiffMap\x12\x31\n\x03map\x18\x01 \x03(\x0b\x32$.qdrant.VectorParamsDiffMap.MapEntry\x1a\x44\n\x08MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x18.qdrant.VectorParamsDiff:\x02\x38\x01\"p\n\rVectorsConfig\x12&\n\x06params\x18\x01 \x01(\x0b\x32\x14.qdrant.VectorParamsH\x00\x12-\n\nparams_map\x18\x02 \x01(\x0b\x32\x17.qdrant.VectorParamsMapH\x00\x42\x08\n\x06\x63onfig\"|\n\x11VectorsConfigDiff\x12*\n\x06params\x18\x01 \x01(\x0b\x32\x18.qdrant.VectorParamsDiffH\x00\x12\x31\n\nparams_map\x18\x02 \x01(\x0b\x32\x1b.qdrant.VectorParamsDiffMapH\x00\x42\x08\n\x06\x63onfig\"3\n\x18GetCollectionInfoRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\"\x18\n\x16ListCollectionsRequest\"%\n\x15\x43ollectionDescription\x12\x0c\n\x04name\x18\x01 \x01(\t\"Q\n\x19GetCollectionInfoResponse\x12&\n\x06result\x18\x01 \x01(\x0b\x32\x16.qdrant.CollectionInfo\x12\x0c\n\x04time\x18\x02 \x01(\x01\"[\n\x17ListCollectionsResponse\x12\x32\n\x0b\x63ollections\x18\x01 \x03(\x0b\x32\x1d.qdrant.CollectionDescription\x12\x0c\n\x04time\x18\x02 \x01(\x01\",\n\x0fOptimizerStatus\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\x90\x02\n\x0eHnswConfigDiff\x12\x0e\n\x01m\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x19\n\x0c\x65\x66_construct\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12 \n\x13\x66ull_scan_threshold\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12!\n\x14max_indexing_threads\x18\x04 \x01(\x04H\x03\x88\x01\x01\x12\x14\n\x07on_disk\x18\x05 \x01(\x08H\x04\x88\x01\x01\x12\x16\n\tpayload_m\x18\x06 \x01(\x04H\x05\x88\x01\x01\x42\x04\n\x02_mB\x0f\n\r_ef_constructB\x16\n\x14_full_scan_thresholdB\x17\n\x15_max_indexing_threadsB\n\n\x08_on_diskB\x0c\n\n_payload_m\"y\n\rWalConfigDiff\x12\x1c\n\x0fwal_capacity_mb\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1f\n\x12wal_segments_ahead\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x12\n\x10_wal_capacity_mbB\x15\n\x13_wal_segments_ahead\"\xec\x03\n\x14OptimizersConfigDiff\x12\x1e\n\x11\x64\x65leted_threshold\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12%\n\x18vacuum_min_vector_number\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12#\n\x16\x64\x65\x66\x61ult_segment_number\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x1d\n\x10max_segment_size\x18\x04 \x01(\x04H\x03\x88\x01\x01\x12\x1d\n\x10memmap_threshold\x18\x05 \x01(\x04H\x04\x88\x01\x01\x12\x1f\n\x12indexing_threshold\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x1f\n\x12\x66lush_interval_sec\x18\x07 \x01(\x04H\x06\x88\x01\x01\x12%\n\x18max_optimization_threads\x18\x08 \x01(\x04H\x07\x88\x01\x01\x42\x14\n\x12_deleted_thresholdB\x1b\n\x19_vacuum_min_vector_numberB\x19\n\x17_default_segment_numberB\x13\n\x11_max_segment_sizeB\x13\n\x11_memmap_thresholdB\x15\n\x13_indexing_thresholdB\x15\n\x13_flush_interval_secB\x1b\n\x19_max_optimization_threads\"\x88\x01\n\x12ScalarQuantization\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.qdrant.QuantizationType\x12\x15\n\x08quantile\x18\x02 \x01(\x02H\x00\x88\x01\x01\x12\x17\n\nalways_ram\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x0b\n\t_quantileB\r\n\x0b_always_ram\"l\n\x13ProductQuantization\x12-\n\x0b\x63ompression\x18\x01 \x01(\x0e\x32\x18.qdrant.CompressionRatio\x12\x17\n\nalways_ram\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_always_ram\"<\n\x12\x42inaryQuantization\x12\x17\n\nalways_ram\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\r\n\x0b_always_ram\"\xb0\x01\n\x12QuantizationConfig\x12,\n\x06scalar\x18\x01 \x01(\x0b\x32\x1a.qdrant.ScalarQuantizationH\x00\x12.\n\x07product\x18\x02 \x01(\x0b\x32\x1b.qdrant.ProductQuantizationH\x00\x12,\n\x06\x62inary\x18\x03 \x01(\x0b\x32\x1a.qdrant.BinaryQuantizationH\x00\x42\x0e\n\x0cquantization\"\n\n\x08\x44isabled\"\xda\x01\n\x16QuantizationConfigDiff\x12,\n\x06scalar\x18\x01 \x01(\x0b\x32\x1a.qdrant.ScalarQuantizationH\x00\x12.\n\x07product\x18\x02 \x01(\x0b\x32\x1b.qdrant.ProductQuantizationH\x00\x12$\n\x08\x64isabled\x18\x03 \x01(\x0b\x32\x10.qdrant.DisabledH\x00\x12,\n\x06\x62inary\x18\x04 \x01(\x0b\x32\x1a.qdrant.BinaryQuantizationH\x00\x42\x0e\n\x0cquantization\"\xe1\x05\n\x10\x43reateCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x30\n\x0bhnsw_config\x18\x04 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x00\x88\x01\x01\x12.\n\nwal_config\x18\x05 \x01(\x0b\x32\x15.qdrant.WalConfigDiffH\x01\x88\x01\x01\x12<\n\x11optimizers_config\x18\x06 \x01(\x0b\x32\x1c.qdrant.OptimizersConfigDiffH\x02\x88\x01\x01\x12\x19\n\x0cshard_number\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fon_disk_payload\x18\x08 \x01(\x08H\x04\x88\x01\x01\x12\x14\n\x07timeout\x18\t \x01(\x04H\x05\x88\x01\x01\x12\x32\n\x0evectors_config\x18\n \x01(\x0b\x32\x15.qdrant.VectorsConfigH\x06\x88\x01\x01\x12\x1f\n\x12replication_factor\x18\x0b \x01(\rH\x07\x88\x01\x01\x12%\n\x18write_consistency_factor\x18\x0c \x01(\rH\x08\x88\x01\x01\x12!\n\x14init_from_collection\x18\r \x01(\tH\t\x88\x01\x01\x12<\n\x13quantization_config\x18\x0e \x01(\x0b\x32\x1a.qdrant.QuantizationConfigH\n\x88\x01\x01\x42\x0e\n\x0c_hnsw_configB\r\n\x0b_wal_configB\x14\n\x12_optimizers_configB\x0f\n\r_shard_numberB\x12\n\x10_on_disk_payloadB\n\n\x08_timeoutB\x11\n\x0f_vectors_configB\x15\n\x13_replication_factorB\x1b\n\x19_write_consistency_factorB\x17\n\x15_init_from_collectionB\x16\n\x14_quantization_configJ\x04\x08\x02\x10\x03J\x04\x08\x03\x10\x04\"\xc6\x03\n\x10UpdateCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12<\n\x11optimizers_config\x18\x02 \x01(\x0b\x32\x1c.qdrant.OptimizersConfigDiffH\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x31\n\x06params\x18\x04 \x01(\x0b\x32\x1c.qdrant.CollectionParamsDiffH\x02\x88\x01\x01\x12\x30\n\x0bhnsw_config\x18\x05 \x01(\x0b\x32\x16.qdrant.HnswConfigDiffH\x03\x88\x01\x01\x12\x36\n\x0evectors_config\x18\x06 \x01(\x0b\x32\x19.qdrant.VectorsConfigDiffH\x04\x88\x01\x01\x12@\n\x13quantization_config\x18\x07 \x01(\x0b\x32\x1e.qdrant.QuantizationConfigDiffH\x05\x88\x01\x01\x42\x14\n\x12_optimizers_configB\n\n\x08_timeoutB\t\n\x07_paramsB\x0e\n\x0c_hnsw_configB\x11\n\x0f_vectors_configB\x16\n\x14_quantization_config\"M\n\x10\x44\x65leteCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_timeout\";\n\x1b\x43ollectionOperationResponse\x12\x0e\n\x06result\x18\x01 \x01(\x08\x12\x0c\n\x04time\x18\x02 \x01(\x01\"\xca\x02\n\x10\x43ollectionParams\x12\x14\n\x0cshard_number\x18\x03 \x01(\r\x12\x17\n\x0fon_disk_payload\x18\x04 \x01(\x08\x12\x32\n\x0evectors_config\x18\x05 \x01(\x0b\x32\x15.qdrant.VectorsConfigH\x00\x88\x01\x01\x12\x1f\n\x12replication_factor\x18\x06 \x01(\rH\x01\x88\x01\x01\x12%\n\x18write_consistency_factor\x18\x07 \x01(\rH\x02\x88\x01\x01\x12 \n\x13read_fan_out_factor\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x11\n\x0f_vectors_configB\x15\n\x13_replication_factorB\x1b\n\x19_write_consistency_factorB\x16\n\x14_read_fan_out_factorJ\x04\x08\x01\x10\x02J\x04\x08\x02\x10\x03\"\xfe\x01\n\x14\x43ollectionParamsDiff\x12\x1f\n\x12replication_factor\x18\x01 \x01(\rH\x00\x88\x01\x01\x12%\n\x18write_consistency_factor\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x0fon_disk_payload\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12 \n\x13read_fan_out_factor\x18\x04 \x01(\rH\x03\x88\x01\x01\x42\x15\n\x13_replication_factorB\x1b\n\x19_write_consistency_factorB\x12\n\x10_on_disk_payloadB\x16\n\x14_read_fan_out_factor\"\xa2\x02\n\x10\x43ollectionConfig\x12(\n\x06params\x18\x01 \x01(\x0b\x32\x18.qdrant.CollectionParams\x12+\n\x0bhnsw_config\x18\x02 \x01(\x0b\x32\x16.qdrant.HnswConfigDiff\x12\x36\n\x10optimizer_config\x18\x03 \x01(\x0b\x32\x1c.qdrant.OptimizersConfigDiff\x12)\n\nwal_config\x18\x04 \x01(\x0b\x32\x15.qdrant.WalConfigDiff\x12<\n\x13quantization_config\x18\x05 \x01(\x0b\x32\x1a.qdrant.QuantizationConfigH\x00\x88\x01\x01\x42\x16\n\x14_quantization_config\"\xbd\x01\n\x0fTextIndexParams\x12(\n\ttokenizer\x18\x01 \x01(\x0e\x32\x15.qdrant.TokenizerType\x12\x16\n\tlowercase\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rmin_token_len\x18\x03 \x01(\x04H\x01\x88\x01\x01\x12\x1a\n\rmax_token_len\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\x0c\n\n_lowercaseB\x10\n\x0e_min_token_lenB\x10\n\x0e_max_token_len\"Z\n\x12PayloadIndexParams\x12\x34\n\x11text_index_params\x18\x01 \x01(\x0b\x32\x17.qdrant.TextIndexParamsH\x00\x42\x0e\n\x0cindex_params\"\x9d\x01\n\x11PayloadSchemaInfo\x12,\n\tdata_type\x18\x01 \x01(\x0e\x32\x19.qdrant.PayloadSchemaType\x12/\n\x06params\x18\x02 \x01(\x0b\x32\x1a.qdrant.PayloadIndexParamsH\x00\x88\x01\x01\x12\x13\n\x06points\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\t\n\x07_paramsB\t\n\x07_points\"\xba\x03\n\x0e\x43ollectionInfo\x12(\n\x06status\x18\x01 \x01(\x0e\x32\x18.qdrant.CollectionStatus\x12\x31\n\x10optimizer_status\x18\x02 \x01(\x0b\x32\x17.qdrant.OptimizerStatus\x12\x15\n\rvectors_count\x18\x03 \x01(\x04\x12\x16\n\x0esegments_count\x18\x04 \x01(\x04\x12(\n\x06\x63onfig\x18\x07 \x01(\x0b\x32\x18.qdrant.CollectionConfig\x12\x41\n\x0epayload_schema\x18\x08 \x03(\x0b\x32).qdrant.CollectionInfo.PayloadSchemaEntry\x12\x14\n\x0cpoints_count\x18\t \x01(\x04\x12\"\n\x15indexed_vectors_count\x18\n \x01(\x04H\x00\x88\x01\x01\x1aO\n\x12PayloadSchemaEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.qdrant.PayloadSchemaInfo:\x02\x38\x01\x42\x18\n\x16_indexed_vectors_countJ\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07\"[\n\rChangeAliases\x12(\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32\x17.qdrant.AliasOperations\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\n\n\x08_timeout\"\xa2\x01\n\x0f\x41liasOperations\x12+\n\x0c\x63reate_alias\x18\x01 \x01(\x0b\x32\x13.qdrant.CreateAliasH\x00\x12+\n\x0crename_alias\x18\x02 \x01(\x0b\x32\x13.qdrant.RenameAliasH\x00\x12+\n\x0c\x64\x65lete_alias\x18\x03 \x01(\x0b\x32\x13.qdrant.DeleteAliasH\x00\x42\x08\n\x06\x61\x63tion\":\n\x0b\x43reateAlias\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x12\n\nalias_name\x18\x02 \x01(\t\"=\n\x0bRenameAlias\x12\x16\n\x0eold_alias_name\x18\x01 \x01(\t\x12\x16\n\x0enew_alias_name\x18\x02 \x01(\t\"!\n\x0b\x44\x65leteAlias\x12\x12\n\nalias_name\x18\x01 \x01(\t\"\x14\n\x12ListAliasesRequest\"7\n\x1cListCollectionAliasesRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\"?\n\x10\x41liasDescription\x12\x12\n\nalias_name\x18\x01 \x01(\t\x12\x17\n\x0f\x63ollection_name\x18\x02 \x01(\t\"N\n\x13ListAliasesResponse\x12)\n\x07\x61liases\x18\x01 \x03(\x0b\x32\x18.qdrant.AliasDescription\x12\x0c\n\x04time\x18\x02 \x01(\x01\"7\n\x1c\x43ollectionClusterInfoRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\"]\n\x0eLocalShardInfo\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x14\n\x0cpoints_count\x18\x02 \x01(\x04\x12#\n\x05state\x18\x03 \x01(\x0e\x32\x14.qdrant.ReplicaState\"Y\n\x0fRemoteShardInfo\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x0f\n\x07peer_id\x18\x02 \x01(\x04\x12#\n\x05state\x18\x03 \x01(\x0e\x32\x14.qdrant.ReplicaState\"M\n\x11ShardTransferInfo\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x0c\n\x04\x66rom\x18\x02 \x01(\x04\x12\n\n\x02to\x18\x03 \x01(\x04\x12\x0c\n\x04sync\x18\x04 \x01(\x08\"\xd7\x01\n\x1d\x43ollectionClusterInfoResponse\x12\x0f\n\x07peer_id\x18\x01 \x01(\x04\x12\x13\n\x0bshard_count\x18\x02 \x01(\x04\x12,\n\x0clocal_shards\x18\x03 \x03(\x0b\x32\x16.qdrant.LocalShardInfo\x12.\n\rremote_shards\x18\x04 \x03(\x0b\x32\x17.qdrant.RemoteShardInfo\x12\x32\n\x0fshard_transfers\x18\x05 \x03(\x0b\x32\x19.qdrant.ShardTransferInfo\"G\n\tMoveShard\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x14\n\x0c\x66rom_peer_id\x18\x02 \x01(\x04\x12\x12\n\nto_peer_id\x18\x03 \x01(\x04\",\n\x07Replica\x12\x10\n\x08shard_id\x18\x01 \x01(\r\x12\x0f\n\x07peer_id\x18\x02 \x01(\x04\"\x9a\x02\n#UpdateCollectionClusterSetupRequest\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\'\n\nmove_shard\x18\x02 \x01(\x0b\x32\x11.qdrant.MoveShardH\x00\x12,\n\x0freplicate_shard\x18\x03 \x01(\x0b\x32\x11.qdrant.MoveShardH\x00\x12+\n\x0e\x61\x62ort_transfer\x18\x04 \x01(\x0b\x32\x11.qdrant.MoveShardH\x00\x12\'\n\x0c\x64rop_replica\x18\x05 \x01(\x0b\x32\x0f.qdrant.ReplicaH\x00\x12\x14\n\x07timeout\x18\x06 \x01(\x04H\x01\x88\x01\x01\x42\x0b\n\toperationB\n\n\x08_timeout\"6\n$UpdateCollectionClusterSetupResponse\x12\x0e\n\x06result\x18\x01 \x01(\x08*@\n\x08\x44istance\x12\x13\n\x0fUnknownDistance\x10\x00\x12\n\n\x06\x43osine\x10\x01\x12\n\n\x06\x45uclid\x10\x02\x12\x07\n\x03\x44ot\x10\x03*O\n\x10\x43ollectionStatus\x12\x1b\n\x17UnknownCollectionStatus\x10\x00\x12\t\n\x05Green\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\x07\n\x03Red\x10\x03*f\n\x11PayloadSchemaType\x12\x0f\n\x0bUnknownType\x10\x00\x12\x0b\n\x07Keyword\x10\x01\x12\x0b\n\x07Integer\x10\x02\x12\t\n\x05\x46loat\x10\x03\x12\x07\n\x03Geo\x10\x04\x12\x08\n\x04Text\x10\x05\x12\x08\n\x04\x42ool\x10\x06*5\n\x10QuantizationType\x12\x17\n\x13UnknownQuantization\x10\x00\x12\x08\n\x04Int8\x10\x01*=\n\x10\x43ompressionRatio\x12\x06\n\x02x4\x10\x00\x12\x06\n\x02x8\x10\x01\x12\x07\n\x03x16\x10\x02\x12\x07\n\x03x32\x10\x03\x12\x07\n\x03x64\x10\x04*T\n\rTokenizerType\x12\x0b\n\x07Unknown\x10\x00\x12\n\n\x06Prefix\x10\x01\x12\x0e\n\nWhitespace\x10\x02\x12\x08\n\x04Word\x10\x03\x12\x10\n\x0cMultilingual\x10\x04*Q\n\x0cReplicaState\x12\n\n\x06\x41\x63tive\x10\x00\x12\x08\n\x04\x44\x65\x61\x64\x10\x01\x12\x0b\n\x07Partial\x10\x02\x12\x10\n\x0cInitializing\x10\x03\x12\x0c\n\x08Listener\x10\x04\x62\x06proto3') _DISTANCE = DESCRIPTOR.enum_types_by_name['Distance'] Distance = enum_type_wrapper.EnumTypeWrapper(_DISTANCE) @@ -501,20 +501,20 @@ _VECTORPARAMSDIFFMAP_MAPENTRY._serialized_options = b'8\001' _COLLECTIONINFO_PAYLOADSCHEMAENTRY._options = None _COLLECTIONINFO_PAYLOADSCHEMAENTRY._serialized_options = b'8\001' - _DISTANCE._serialized_start=7600 - _DISTANCE._serialized_end=7664 - _COLLECTIONSTATUS._serialized_start=7666 - _COLLECTIONSTATUS._serialized_end=7745 - _PAYLOADSCHEMATYPE._serialized_start=7747 - _PAYLOADSCHEMATYPE._serialized_end=7849 - _QUANTIZATIONTYPE._serialized_start=7851 - _QUANTIZATIONTYPE._serialized_end=7904 - _COMPRESSIONRATIO._serialized_start=7906 - _COMPRESSIONRATIO._serialized_end=7967 - _TOKENIZERTYPE._serialized_start=7969 - _TOKENIZERTYPE._serialized_end=8053 - _REPLICASTATE._serialized_start=8055 - _REPLICASTATE._serialized_end=8136 + _DISTANCE._serialized_start=7716 + _DISTANCE._serialized_end=7780 + _COLLECTIONSTATUS._serialized_start=7782 + _COLLECTIONSTATUS._serialized_end=7861 + _PAYLOADSCHEMATYPE._serialized_start=7863 + _PAYLOADSCHEMATYPE._serialized_end=7965 + _QUANTIZATIONTYPE._serialized_start=7967 + _QUANTIZATIONTYPE._serialized_end=8020 + _COMPRESSIONRATIO._serialized_start=8022 + _COMPRESSIONRATIO._serialized_end=8083 + _TOKENIZERTYPE._serialized_start=8085 + _TOKENIZERTYPE._serialized_end=8169 + _REPLICASTATE._serialized_start=8171 + _REPLICASTATE._serialized_end=8252 _VECTORPARAMS._serialized_start=30 _VECTORPARAMS._serialized_end=280 _VECTORPARAMSDIFF._serialized_start=283 @@ -570,55 +570,55 @@ _COLLECTIONOPERATIONRESPONSE._serialized_start=4243 _COLLECTIONOPERATIONRESPONSE._serialized_end=4302 _COLLECTIONPARAMS._serialized_start=4305 - _COLLECTIONPARAMS._serialized_end=4577 - _COLLECTIONPARAMSDIFF._serialized_start=4580 - _COLLECTIONPARAMSDIFF._serialized_end=4776 - _COLLECTIONCONFIG._serialized_start=4779 - _COLLECTIONCONFIG._serialized_end=5069 - _TEXTINDEXPARAMS._serialized_start=5072 - _TEXTINDEXPARAMS._serialized_end=5261 - _PAYLOADINDEXPARAMS._serialized_start=5263 - _PAYLOADINDEXPARAMS._serialized_end=5353 - _PAYLOADSCHEMAINFO._serialized_start=5356 - _PAYLOADSCHEMAINFO._serialized_end=5513 - _COLLECTIONINFO._serialized_start=5516 - _COLLECTIONINFO._serialized_end=5958 - _COLLECTIONINFO_PAYLOADSCHEMAENTRY._serialized_start=5841 - _COLLECTIONINFO_PAYLOADSCHEMAENTRY._serialized_end=5920 - _CHANGEALIASES._serialized_start=5960 - _CHANGEALIASES._serialized_end=6051 - _ALIASOPERATIONS._serialized_start=6054 - _ALIASOPERATIONS._serialized_end=6216 - _CREATEALIAS._serialized_start=6218 - _CREATEALIAS._serialized_end=6276 - _RENAMEALIAS._serialized_start=6278 - _RENAMEALIAS._serialized_end=6339 - _DELETEALIAS._serialized_start=6341 - _DELETEALIAS._serialized_end=6374 - _LISTALIASESREQUEST._serialized_start=6376 - _LISTALIASESREQUEST._serialized_end=6396 - _LISTCOLLECTIONALIASESREQUEST._serialized_start=6398 - _LISTCOLLECTIONALIASESREQUEST._serialized_end=6453 - _ALIASDESCRIPTION._serialized_start=6455 - _ALIASDESCRIPTION._serialized_end=6518 - _LISTALIASESRESPONSE._serialized_start=6520 - _LISTALIASESRESPONSE._serialized_end=6598 - _COLLECTIONCLUSTERINFOREQUEST._serialized_start=6600 - _COLLECTIONCLUSTERINFOREQUEST._serialized_end=6655 - _LOCALSHARDINFO._serialized_start=6657 - _LOCALSHARDINFO._serialized_end=6750 - _REMOTESHARDINFO._serialized_start=6752 - _REMOTESHARDINFO._serialized_end=6841 - _SHARDTRANSFERINFO._serialized_start=6843 - _SHARDTRANSFERINFO._serialized_end=6920 - _COLLECTIONCLUSTERINFORESPONSE._serialized_start=6923 - _COLLECTIONCLUSTERINFORESPONSE._serialized_end=7138 - _MOVESHARD._serialized_start=7140 - _MOVESHARD._serialized_end=7211 - _REPLICA._serialized_start=7213 - _REPLICA._serialized_end=7257 - _UPDATECOLLECTIONCLUSTERSETUPREQUEST._serialized_start=7260 - _UPDATECOLLECTIONCLUSTERSETUPREQUEST._serialized_end=7542 - _UPDATECOLLECTIONCLUSTERSETUPRESPONSE._serialized_start=7544 - _UPDATECOLLECTIONCLUSTERSETUPRESPONSE._serialized_end=7598 + _COLLECTIONPARAMS._serialized_end=4635 + _COLLECTIONPARAMSDIFF._serialized_start=4638 + _COLLECTIONPARAMSDIFF._serialized_end=4892 + _COLLECTIONCONFIG._serialized_start=4895 + _COLLECTIONCONFIG._serialized_end=5185 + _TEXTINDEXPARAMS._serialized_start=5188 + _TEXTINDEXPARAMS._serialized_end=5377 + _PAYLOADINDEXPARAMS._serialized_start=5379 + _PAYLOADINDEXPARAMS._serialized_end=5469 + _PAYLOADSCHEMAINFO._serialized_start=5472 + _PAYLOADSCHEMAINFO._serialized_end=5629 + _COLLECTIONINFO._serialized_start=5632 + _COLLECTIONINFO._serialized_end=6074 + _COLLECTIONINFO_PAYLOADSCHEMAENTRY._serialized_start=5957 + _COLLECTIONINFO_PAYLOADSCHEMAENTRY._serialized_end=6036 + _CHANGEALIASES._serialized_start=6076 + _CHANGEALIASES._serialized_end=6167 + _ALIASOPERATIONS._serialized_start=6170 + _ALIASOPERATIONS._serialized_end=6332 + _CREATEALIAS._serialized_start=6334 + _CREATEALIAS._serialized_end=6392 + _RENAMEALIAS._serialized_start=6394 + _RENAMEALIAS._serialized_end=6455 + _DELETEALIAS._serialized_start=6457 + _DELETEALIAS._serialized_end=6490 + _LISTALIASESREQUEST._serialized_start=6492 + _LISTALIASESREQUEST._serialized_end=6512 + _LISTCOLLECTIONALIASESREQUEST._serialized_start=6514 + _LISTCOLLECTIONALIASESREQUEST._serialized_end=6569 + _ALIASDESCRIPTION._serialized_start=6571 + _ALIASDESCRIPTION._serialized_end=6634 + _LISTALIASESRESPONSE._serialized_start=6636 + _LISTALIASESRESPONSE._serialized_end=6714 + _COLLECTIONCLUSTERINFOREQUEST._serialized_start=6716 + _COLLECTIONCLUSTERINFOREQUEST._serialized_end=6771 + _LOCALSHARDINFO._serialized_start=6773 + _LOCALSHARDINFO._serialized_end=6866 + _REMOTESHARDINFO._serialized_start=6868 + _REMOTESHARDINFO._serialized_end=6957 + _SHARDTRANSFERINFO._serialized_start=6959 + _SHARDTRANSFERINFO._serialized_end=7036 + _COLLECTIONCLUSTERINFORESPONSE._serialized_start=7039 + _COLLECTIONCLUSTERINFORESPONSE._serialized_end=7254 + _MOVESHARD._serialized_start=7256 + _MOVESHARD._serialized_end=7327 + _REPLICA._serialized_start=7329 + _REPLICA._serialized_end=7373 + _UPDATECOLLECTIONCLUSTERSETUPREQUEST._serialized_start=7376 + _UPDATECOLLECTIONCLUSTERSETUPREQUEST._serialized_end=7658 + _UPDATECOLLECTIONCLUSTERSETUPRESPONSE._serialized_start=7660 + _UPDATECOLLECTIONCLUSTERSETUPRESPONSE._serialized_end=7714 # @@protoc_insertion_point(module_scope) diff --git a/qdrant_client/grpc/points_pb2.py b/qdrant_client/grpc/points_pb2.py index 56d40c7f..3f4ba34d 100644 --- a/qdrant_client/grpc/points_pb2.py +++ b/qdrant_client/grpc/points_pb2.py @@ -17,7 +17,7 @@ from . import collections_pb2 as collections__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cpoints.proto\x12\x06qdrant\x1a\x13json_with_int.proto\x1a\x11\x63ollections.proto\"8\n\rWriteOrdering\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.qdrant.WriteOrderingType\"Y\n\x0fReadConsistency\x12+\n\x04type\x18\x01 \x01(\x0e\x32\x1b.qdrant.ReadConsistencyTypeH\x00\x12\x10\n\x06\x66\x61\x63tor\x18\x02 \x01(\x04H\x00\x42\x07\n\x05value\"<\n\x07PointId\x12\r\n\x03num\x18\x01 \x01(\x04H\x00\x12\x0e\n\x04uuid\x18\x02 \x01(\tH\x00\x42\x12\n\x10point_id_options\"\x16\n\x06Vector\x12\x0c\n\x04\x64\x61ta\x18\x01 \x03(\x02\"\xa3\x01\n\x0cUpsertPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12#\n\x06points\x18\x03 \x03(\x0b\x32\x13.qdrant.PointStruct\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\xa6\x01\n\x0c\x44\x65letePoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12&\n\x06points\x18\x03 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\x91\x02\n\tGetPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x1c\n\x03ids\x18\x02 \x03(\x0b\x32\x0f.qdrant.PointId\x12\x31\n\x0cwith_payload\x18\x04 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12\x36\n\x0cwith_vectors\x18\x05 \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x00\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x06 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x01\x88\x01\x01\x42\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyJ\x04\x08\x03\x10\x04\"\xaa\x01\n\x12UpdatePointVectors\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12$\n\x06points\x18\x03 \x03(\x0b\x32\x14.qdrant.PointVectors\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"M\n\x0cPointVectors\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12 \n\x07vectors\x18\x02 \x01(\x0b\x32\x0f.qdrant.Vectors\"\xdf\x01\n\x12\x44\x65letePointVectors\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12/\n\x0fpoints_selector\x18\x03 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12(\n\x07vectors\x18\x04 \x01(\x0b\x32\x17.qdrant.VectorsSelector\x12,\n\x08ordering\x18\x05 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\xc9\x02\n\x10SetPayloadPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x36\n\x07payload\x18\x03 \x03(\x0b\x32%.qdrant.SetPayloadPoints.PayloadEntry\x12\x34\n\x0fpoints_selector\x18\x05 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x01\x88\x01\x01\x12,\n\x08ordering\x18\x06 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x02\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\x07\n\x05_waitB\x12\n\x10_points_selectorB\x0b\n\t_orderingJ\x04\x08\x04\x10\x05\"\xe3\x01\n\x13\x44\x65letePayloadPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x0c\n\x04keys\x18\x03 \x03(\t\x12\x34\n\x0fpoints_selector\x18\x05 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x01\x88\x01\x01\x12,\n\x08ordering\x18\x06 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x02\x88\x01\x01\x42\x07\n\x05_waitB\x12\n\x10_points_selectorB\x0b\n\t_orderingJ\x04\x08\x04\x10\x05\"\xac\x01\n\x12\x43learPayloadPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12&\n\x06points\x18\x03 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\xaf\x02\n\x1a\x43reateFieldIndexCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x12\n\nfield_name\x18\x03 \x01(\t\x12*\n\nfield_type\x18\x04 \x01(\x0e\x32\x11.qdrant.FieldTypeH\x01\x88\x01\x01\x12;\n\x12\x66ield_index_params\x18\x05 \x01(\x0b\x32\x1a.qdrant.PayloadIndexParamsH\x02\x88\x01\x01\x12,\n\x08ordering\x18\x06 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x03\x88\x01\x01\x42\x07\n\x05_waitB\r\n\x0b_field_typeB\x15\n\x13_field_index_paramsB\x0b\n\t_ordering\"\xa0\x01\n\x1a\x44\x65leteFieldIndexCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x12\n\nfield_name\x18\x03 \x01(\t\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"(\n\x16PayloadIncludeSelector\x12\x0e\n\x06\x66ields\x18\x01 \x03(\t\"(\n\x16PayloadExcludeSelector\x12\x0e\n\x06\x66ields\x18\x01 \x03(\t\"\xa1\x01\n\x13WithPayloadSelector\x12\x10\n\x06\x65nable\x18\x01 \x01(\x08H\x00\x12\x31\n\x07include\x18\x02 \x01(\x0b\x32\x1e.qdrant.PayloadIncludeSelectorH\x00\x12\x31\n\x07\x65xclude\x18\x03 \x01(\x0b\x32\x1e.qdrant.PayloadExcludeSelectorH\x00\x42\x12\n\x10selector_options\"\x82\x01\n\x0cNamedVectors\x12\x32\n\x07vectors\x18\x01 \x03(\x0b\x32!.qdrant.NamedVectors.VectorsEntry\x1a>\n\x0cVectorsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.qdrant.Vector:\x02\x38\x01\"g\n\x07Vectors\x12 \n\x06vector\x18\x01 \x01(\x0b\x32\x0e.qdrant.VectorH\x00\x12\'\n\x07vectors\x18\x02 \x01(\x0b\x32\x14.qdrant.NamedVectorsH\x00\x42\x11\n\x0fvectors_options\" \n\x0fVectorsSelector\x12\r\n\x05names\x18\x01 \x03(\t\"g\n\x13WithVectorsSelector\x12\x10\n\x06\x65nable\x18\x01 \x01(\x08H\x00\x12*\n\x07include\x18\x02 \x01(\x0b\x32\x17.qdrant.VectorsSelectorH\x00\x42\x12\n\x10selector_options\"\x88\x01\n\x18QuantizationSearchParams\x12\x13\n\x06ignore\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x14\n\x07rescore\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12\x19\n\x0coversampling\x18\x03 \x01(\x01H\x02\x88\x01\x01\x42\t\n\x07_ignoreB\n\n\x08_rescoreB\x0f\n\r_oversampling\"\xc8\x01\n\x0cSearchParams\x12\x14\n\x07hnsw_ef\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x12\n\x05\x65xact\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12;\n\x0cquantization\x18\x03 \x01(\x0b\x32 .qdrant.QuantizationSearchParamsH\x02\x88\x01\x01\x12\x19\n\x0cindexed_only\x18\x04 \x01(\x08H\x03\x88\x01\x01\x42\n\n\x08_hnsw_efB\x08\n\x06_exactB\x0f\n\r_quantizationB\x0f\n\r_indexed_only\"\xd7\x03\n\x0cSearchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x0e\n\x06vector\x18\x02 \x03(\x02\x12\x1e\n\x06\x66ilter\x18\x03 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x04 \x01(\x04\x12\x31\n\x0cwith_payload\x18\x06 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x07 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\x08 \x01(\x02H\x00\x88\x01\x01\x12\x13\n\x06offset\x18\t \x01(\x04H\x01\x88\x01\x01\x12\x18\n\x0bvector_name\x18\n \x01(\tH\x02\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\x0b \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x03\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x0c \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x04\x88\x01\x01\x42\x12\n\x10_score_thresholdB\t\n\x07_offsetB\x0e\n\x0c_vector_nameB\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyJ\x04\x08\x05\x10\x06\"\xa6\x01\n\x11SearchBatchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12+\n\rsearch_points\x18\x02 \x03(\x0b\x32\x14.qdrant.SearchPoints\x12\x36\n\x10read_consistency\x18\x03 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x00\x88\x01\x01\x42\x13\n\x11_read_consistency\"\xb2\x01\n\nWithLookup\x12\x12\n\ncollection\x18\x01 \x01(\t\x12\x36\n\x0cwith_payload\x18\x02 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelectorH\x00\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\x03 \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x01\x88\x01\x01\x42\x0f\n\r_with_payloadB\x0f\n\r_with_vectors\"\x9a\x04\n\x11SearchPointGroups\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x0e\n\x06vector\x18\x02 \x03(\x02\x12\x1e\n\x06\x66ilter\x18\x03 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x04 \x01(\r\x12\x31\n\x0cwith_payload\x18\x05 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x06 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\x07 \x01(\x02H\x00\x88\x01\x01\x12\x18\n\x0bvector_name\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\t \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x02\x88\x01\x01\x12\x10\n\x08group_by\x18\n \x01(\t\x12\x12\n\ngroup_size\x18\x0b \x01(\r\x12\x36\n\x10read_consistency\x18\x0c \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x03\x88\x01\x01\x12,\n\x0bwith_lookup\x18\r \x01(\x0b\x32\x12.qdrant.WithLookupH\x04\x88\x01\x01\x42\x12\n\x10_score_thresholdB\x0e\n\x0c_vector_nameB\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyB\x0e\n\x0c_with_lookup\"\xe5\x02\n\x0cScrollPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x1e\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.Filter\x12$\n\x06offset\x18\x03 \x01(\x0b\x32\x0f.qdrant.PointIdH\x00\x88\x01\x01\x12\x12\n\x05limit\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x31\n\x0cwith_payload\x18\x06 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12\x36\n\x0cwith_vectors\x18\x07 \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x02\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x08 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x03\x88\x01\x01\x42\t\n\x07_offsetB\x08\n\x06_limitB\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyJ\x04\x08\x05\x10\x06\"S\n\x0eLookupLocation\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x18\n\x0bvector_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_vector_name\"\xc6\x04\n\x0fRecommendPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12!\n\x08positive\x18\x02 \x03(\x0b\x32\x0f.qdrant.PointId\x12!\n\x08negative\x18\x03 \x03(\x0b\x32\x0f.qdrant.PointId\x12\x1e\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x05 \x01(\x04\x12\x31\n\x0cwith_payload\x18\x07 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x08 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\t \x01(\x02H\x00\x88\x01\x01\x12\x13\n\x06offset\x18\n \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05using\x18\x0b \x01(\tH\x02\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\x0c \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x03\x88\x01\x01\x12\x30\n\x0blookup_from\x18\r \x01(\x0b\x32\x16.qdrant.LookupLocationH\x04\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x0e \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x05\x88\x01\x01\x42\x12\n\x10_score_thresholdB\t\n\x07_offsetB\x08\n\x06_usingB\x0f\n\r_with_vectorsB\x0e\n\x0c_lookup_fromB\x13\n\x11_read_consistencyJ\x04\x08\x06\x10\x07\"\xaf\x01\n\x14RecommendBatchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x31\n\x10recommend_points\x18\x02 \x03(\x0b\x32\x17.qdrant.RecommendPoints\x12\x36\n\x10read_consistency\x18\x03 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x00\x88\x01\x01\x42\x13\n\x11_read_consistency\"\x89\x05\n\x14RecommendPointGroups\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12!\n\x08positive\x18\x02 \x03(\x0b\x32\x0f.qdrant.PointId\x12!\n\x08negative\x18\x03 \x03(\x0b\x32\x0f.qdrant.PointId\x12\x1e\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x05 \x01(\r\x12\x31\n\x0cwith_payload\x18\x06 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x07 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\x08 \x01(\x02H\x00\x88\x01\x01\x12\x12\n\x05using\x18\t \x01(\tH\x01\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\n \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x02\x88\x01\x01\x12\x30\n\x0blookup_from\x18\x0b \x01(\x0b\x32\x16.qdrant.LookupLocationH\x03\x88\x01\x01\x12\x10\n\x08group_by\x18\x0c \x01(\t\x12\x12\n\ngroup_size\x18\r \x01(\r\x12\x36\n\x10read_consistency\x18\x0e \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x04\x88\x01\x01\x12,\n\x0bwith_lookup\x18\x0f \x01(\x0b\x32\x12.qdrant.WithLookupH\x05\x88\x01\x01\x42\x12\n\x10_score_thresholdB\x08\n\x06_usingB\x0f\n\r_with_vectorsB\x0e\n\x0c_lookup_fromB\x13\n\x11_read_consistencyB\x0e\n\x0c_with_lookup\"d\n\x0b\x43ountPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x1e\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.Filter\x12\x12\n\x05\x65xact\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_exact\"\xc1\x08\n\x15PointsUpdateOperation\x12?\n\x06upsert\x18\x01 \x01(\x0b\x32-.qdrant.PointsUpdateOperation.PointStructListH\x00\x12(\n\x06\x64\x65lete\x18\x02 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x12?\n\x0bset_payload\x18\x03 \x01(\x0b\x32(.qdrant.PointsUpdateOperation.SetPayloadH\x00\x12\x45\n\x11overwrite_payload\x18\x04 \x01(\x0b\x32(.qdrant.PointsUpdateOperation.SetPayloadH\x00\x12\x45\n\x0e\x64\x65lete_payload\x18\x05 \x01(\x0b\x32+.qdrant.PointsUpdateOperation.DeletePayloadH\x00\x12/\n\rclear_payload\x18\x06 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x12\x45\n\x0eupdate_vectors\x18\x07 \x01(\x0b\x32+.qdrant.PointsUpdateOperation.UpdateVectorsH\x00\x12\x45\n\x0e\x64\x65lete_vectors\x18\x08 \x01(\x0b\x32+.qdrant.PointsUpdateOperation.DeleteVectorsH\x00\x1a\x36\n\x0fPointStructList\x12#\n\x06points\x18\x01 \x03(\x0b\x32\x13.qdrant.PointStruct\x1a\xdd\x01\n\nSetPayload\x12\x46\n\x07payload\x18\x01 \x03(\x0b\x32\x35.qdrant.PointsUpdateOperation.SetPayload.PayloadEntry\x12\x34\n\x0fpoints_selector\x18\x02 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\x12\n\x10_points_selector\x1ag\n\rDeletePayload\x12\x0c\n\x04keys\x18\x01 \x03(\t\x12\x34\n\x0fpoints_selector\x18\x02 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x88\x01\x01\x42\x12\n\x10_points_selector\x1a\x35\n\rUpdateVectors\x12$\n\x06points\x18\x01 \x03(\x0b\x32\x14.qdrant.PointVectors\x1aj\n\rDeleteVectors\x12/\n\x0fpoints_selector\x18\x01 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12(\n\x07vectors\x18\x02 \x01(\x0b\x32\x17.qdrant.VectorsSelectorB\x0b\n\toperation\"\xb6\x01\n\x11UpdateBatchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x31\n\noperations\x18\x03 \x03(\x0b\x32\x1d.qdrant.PointsUpdateOperation\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"M\n\x17PointsOperationResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.qdrant.UpdateResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"J\n\x0cUpdateResult\x12\x14\n\x0coperation_id\x18\x01 \x01(\x04\x12$\n\x06status\x18\x02 \x01(\x0e\x32\x14.qdrant.UpdateStatus\"\xf5\x01\n\x0bScoredPoint\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12\x31\n\x07payload\x18\x02 \x03(\x0b\x32 .qdrant.ScoredPoint.PayloadEntry\x12\r\n\x05score\x18\x03 \x01(\x02\x12\x0f\n\x07version\x18\x05 \x01(\x04\x12%\n\x07vectors\x18\x06 \x01(\x0b\x32\x0f.qdrant.VectorsH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\n\n\x08_vectorsJ\x04\x08\x04\x10\x05\"\\\n\x07GroupId\x12\x18\n\x0eunsigned_value\x18\x01 \x01(\x04H\x00\x12\x17\n\rinteger_value\x18\x02 \x01(\x03H\x00\x12\x16\n\x0cstring_value\x18\x03 \x01(\tH\x00\x42\x06\n\x04kind\"t\n\nPointGroup\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.GroupId\x12!\n\x04hits\x18\x02 \x03(\x0b\x32\x13.qdrant.ScoredPoint\x12&\n\x06lookup\x18\x03 \x01(\x0b\x32\x16.qdrant.RetrievedPoint\"2\n\x0cGroupsResult\x12\"\n\x06groups\x18\x01 \x03(\x0b\x32\x12.qdrant.PointGroup\"C\n\x0eSearchResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.ScoredPoint\x12\x0c\n\x04time\x18\x02 \x01(\x01\"2\n\x0b\x42\x61tchResult\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.ScoredPoint\"H\n\x13SearchBatchResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.BatchResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"J\n\x14SearchGroupsResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.qdrant.GroupsResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"B\n\rCountResponse\x12#\n\x06result\x18\x01 \x01(\x0b\x32\x13.qdrant.CountResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"\x8b\x01\n\x0eScrollResponse\x12.\n\x10next_page_offset\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointIdH\x00\x88\x01\x01\x12&\n\x06result\x18\x02 \x03(\x0b\x32\x16.qdrant.RetrievedPoint\x12\x0c\n\x04time\x18\x03 \x01(\x01\x42\x13\n\x11_next_page_offset\"\x1c\n\x0b\x43ountResult\x12\r\n\x05\x63ount\x18\x01 \x01(\x04\"\xdb\x01\n\x0eRetrievedPoint\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12\x34\n\x07payload\x18\x02 \x03(\x0b\x32#.qdrant.RetrievedPoint.PayloadEntry\x12%\n\x07vectors\x18\x04 \x01(\x0b\x32\x0f.qdrant.VectorsH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\n\n\x08_vectorsJ\x04\x08\x03\x10\x04\"C\n\x0bGetResponse\x12&\n\x06result\x18\x01 \x03(\x0b\x32\x16.qdrant.RetrievedPoint\x12\x0c\n\x04time\x18\x02 \x01(\x01\"F\n\x11RecommendResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.ScoredPoint\x12\x0c\n\x04time\x18\x02 \x01(\x01\"K\n\x16RecommendBatchResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.BatchResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"M\n\x17RecommendGroupsResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.qdrant.GroupsResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"I\n\x13UpdateBatchResponse\x12$\n\x06result\x18\x01 \x03(\x0b\x32\x14.qdrant.UpdateResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"q\n\x06\x46ilter\x12!\n\x06should\x18\x01 \x03(\x0b\x32\x11.qdrant.Condition\x12\x1f\n\x04must\x18\x02 \x03(\x0b\x32\x11.qdrant.Condition\x12#\n\x08must_not\x18\x03 \x03(\x0b\x32\x11.qdrant.Condition\"\x99\x02\n\tCondition\x12\'\n\x05\x66ield\x18\x01 \x01(\x0b\x32\x16.qdrant.FieldConditionH\x00\x12,\n\x08is_empty\x18\x02 \x01(\x0b\x32\x18.qdrant.IsEmptyConditionH\x00\x12(\n\x06has_id\x18\x03 \x01(\x0b\x32\x16.qdrant.HasIdConditionH\x00\x12 \n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x0e.qdrant.FilterH\x00\x12*\n\x07is_null\x18\x05 \x01(\x0b\x32\x17.qdrant.IsNullConditionH\x00\x12)\n\x06nested\x18\x06 \x01(\x0b\x32\x17.qdrant.NestedConditionH\x00\x42\x12\n\x10\x63ondition_one_of\"\x1f\n\x10IsEmptyCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\"\x1e\n\x0fIsNullCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\"1\n\x0eHasIdCondition\x12\x1f\n\x06has_id\x18\x01 \x03(\x0b\x32\x0f.qdrant.PointId\">\n\x0fNestedCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1e\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.Filter\"\xdd\x01\n\x0e\x46ieldCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05match\x18\x02 \x01(\x0b\x32\r.qdrant.Match\x12\x1c\n\x05range\x18\x03 \x01(\x0b\x32\r.qdrant.Range\x12\x30\n\x10geo_bounding_box\x18\x04 \x01(\x0b\x32\x16.qdrant.GeoBoundingBox\x12%\n\ngeo_radius\x18\x05 \x01(\x0b\x32\x11.qdrant.GeoRadius\x12)\n\x0cvalues_count\x18\x06 \x01(\x0b\x32\x13.qdrant.ValuesCount\"\xa3\x02\n\x05Match\x12\x11\n\x07keyword\x18\x01 \x01(\tH\x00\x12\x11\n\x07integer\x18\x02 \x01(\x03H\x00\x12\x11\n\x07\x62oolean\x18\x03 \x01(\x08H\x00\x12\x0e\n\x04text\x18\x04 \x01(\tH\x00\x12+\n\x08keywords\x18\x05 \x01(\x0b\x32\x17.qdrant.RepeatedStringsH\x00\x12,\n\x08integers\x18\x06 \x01(\x0b\x32\x18.qdrant.RepeatedIntegersH\x00\x12\x33\n\x0f\x65xcept_integers\x18\x07 \x01(\x0b\x32\x18.qdrant.RepeatedIntegersH\x00\x12\x32\n\x0f\x65xcept_keywords\x18\x08 \x01(\x0b\x32\x17.qdrant.RepeatedStringsH\x00\x42\r\n\x0bmatch_value\"\"\n\x0fRepeatedStrings\x12\x0f\n\x07strings\x18\x01 \x03(\t\"$\n\x10RepeatedIntegers\x12\x10\n\x08integers\x18\x01 \x03(\x03\"k\n\x05Range\x12\x0f\n\x02lt\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x0f\n\x02gt\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x10\n\x03gte\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x10\n\x03lte\x18\x04 \x01(\x01H\x03\x88\x01\x01\x42\x05\n\x03_ltB\x05\n\x03_gtB\x06\n\x04_gteB\x06\n\x04_lte\"\\\n\x0eGeoBoundingBox\x12\"\n\x08top_left\x18\x01 \x01(\x0b\x32\x10.qdrant.GeoPoint\x12&\n\x0c\x62ottom_right\x18\x02 \x01(\x0b\x32\x10.qdrant.GeoPoint\"=\n\tGeoRadius\x12 \n\x06\x63\x65nter\x18\x01 \x01(\x0b\x32\x10.qdrant.GeoPoint\x12\x0e\n\x06radius\x18\x02 \x01(\x02\".\n\nGeoPolygon\x12 \n\x06points\x18\x01 \x03(\x0b\x32\x10.qdrant.GeoPoint\"q\n\x0bValuesCount\x12\x0f\n\x02lt\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x0f\n\x02gt\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x10\n\x03gte\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x10\n\x03lte\x18\x04 \x01(\x04H\x03\x88\x01\x01\x42\x05\n\x03_ltB\x05\n\x03_gtB\x06\n\x04_gteB\x06\n\x04_lte\"u\n\x0ePointsSelector\x12\'\n\x06points\x18\x01 \x01(\x0b\x32\x15.qdrant.PointsIdsListH\x00\x12 \n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.FilterH\x00\x42\x18\n\x16points_selector_one_of\"-\n\rPointsIdsList\x12\x1c\n\x03ids\x18\x01 \x03(\x0b\x32\x0f.qdrant.PointId\"\xd5\x01\n\x0bPointStruct\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12\x31\n\x07payload\x18\x03 \x03(\x0b\x32 .qdrant.PointStruct.PayloadEntry\x12%\n\x07vectors\x18\x04 \x01(\x0b\x32\x0f.qdrant.VectorsH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\n\n\x08_vectorsJ\x04\x08\x02\x10\x03\"$\n\x08GeoPoint\x12\x0b\n\x03lon\x18\x01 \x01(\x01\x12\x0b\n\x03lat\x18\x02 \x01(\x01*5\n\x11WriteOrderingType\x12\x08\n\x04Weak\x10\x00\x12\n\n\x06Medium\x10\x01\x12\n\n\x06Strong\x10\x02*8\n\x13ReadConsistencyType\x12\x07\n\x03\x41ll\x10\x00\x12\x0c\n\x08Majority\x10\x01\x12\n\n\x06Quorum\x10\x02*\x83\x01\n\tFieldType\x12\x14\n\x10\x46ieldTypeKeyword\x10\x00\x12\x14\n\x10\x46ieldTypeInteger\x10\x01\x12\x12\n\x0e\x46ieldTypeFloat\x10\x02\x12\x10\n\x0c\x46ieldTypeGeo\x10\x03\x12\x11\n\rFieldTypeText\x10\x04\x12\x11\n\rFieldTypeBool\x10\x05*H\n\x0cUpdateStatus\x12\x17\n\x13UnknownUpdateStatus\x10\x00\x12\x10\n\x0c\x41\x63knowledged\x10\x01\x12\r\n\tCompleted\x10\x02\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cpoints.proto\x12\x06qdrant\x1a\x13json_with_int.proto\x1a\x11\x63ollections.proto\"8\n\rWriteOrdering\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.qdrant.WriteOrderingType\"Y\n\x0fReadConsistency\x12+\n\x04type\x18\x01 \x01(\x0e\x32\x1b.qdrant.ReadConsistencyTypeH\x00\x12\x10\n\x06\x66\x61\x63tor\x18\x02 \x01(\x04H\x00\x42\x07\n\x05value\"<\n\x07PointId\x12\r\n\x03num\x18\x01 \x01(\x04H\x00\x12\x0e\n\x04uuid\x18\x02 \x01(\tH\x00\x42\x12\n\x10point_id_options\"\x16\n\x06Vector\x12\x0c\n\x04\x64\x61ta\x18\x01 \x03(\x02\"\xa3\x01\n\x0cUpsertPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12#\n\x06points\x18\x03 \x03(\x0b\x32\x13.qdrant.PointStruct\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\xa6\x01\n\x0c\x44\x65letePoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12&\n\x06points\x18\x03 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\x91\x02\n\tGetPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x1c\n\x03ids\x18\x02 \x03(\x0b\x32\x0f.qdrant.PointId\x12\x31\n\x0cwith_payload\x18\x04 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12\x36\n\x0cwith_vectors\x18\x05 \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x00\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x06 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x01\x88\x01\x01\x42\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyJ\x04\x08\x03\x10\x04\"\xaa\x01\n\x12UpdatePointVectors\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12$\n\x06points\x18\x03 \x03(\x0b\x32\x14.qdrant.PointVectors\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"M\n\x0cPointVectors\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12 \n\x07vectors\x18\x02 \x01(\x0b\x32\x0f.qdrant.Vectors\"\xdf\x01\n\x12\x44\x65letePointVectors\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12/\n\x0fpoints_selector\x18\x03 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12(\n\x07vectors\x18\x04 \x01(\x0b\x32\x17.qdrant.VectorsSelector\x12,\n\x08ordering\x18\x05 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\xc9\x02\n\x10SetPayloadPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x36\n\x07payload\x18\x03 \x03(\x0b\x32%.qdrant.SetPayloadPoints.PayloadEntry\x12\x34\n\x0fpoints_selector\x18\x05 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x01\x88\x01\x01\x12,\n\x08ordering\x18\x06 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x02\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\x07\n\x05_waitB\x12\n\x10_points_selectorB\x0b\n\t_orderingJ\x04\x08\x04\x10\x05\"\xe3\x01\n\x13\x44\x65letePayloadPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x0c\n\x04keys\x18\x03 \x03(\t\x12\x34\n\x0fpoints_selector\x18\x05 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x01\x88\x01\x01\x12,\n\x08ordering\x18\x06 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x02\x88\x01\x01\x42\x07\n\x05_waitB\x12\n\x10_points_selectorB\x0b\n\t_orderingJ\x04\x08\x04\x10\x05\"\xac\x01\n\x12\x43learPayloadPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12&\n\x06points\x18\x03 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"\xaf\x02\n\x1a\x43reateFieldIndexCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x12\n\nfield_name\x18\x03 \x01(\t\x12*\n\nfield_type\x18\x04 \x01(\x0e\x32\x11.qdrant.FieldTypeH\x01\x88\x01\x01\x12;\n\x12\x66ield_index_params\x18\x05 \x01(\x0b\x32\x1a.qdrant.PayloadIndexParamsH\x02\x88\x01\x01\x12,\n\x08ordering\x18\x06 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x03\x88\x01\x01\x42\x07\n\x05_waitB\r\n\x0b_field_typeB\x15\n\x13_field_index_paramsB\x0b\n\t_ordering\"\xa0\x01\n\x1a\x44\x65leteFieldIndexCollection\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x12\n\nfield_name\x18\x03 \x01(\t\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"(\n\x16PayloadIncludeSelector\x12\x0e\n\x06\x66ields\x18\x01 \x03(\t\"(\n\x16PayloadExcludeSelector\x12\x0e\n\x06\x66ields\x18\x01 \x03(\t\"\xa1\x01\n\x13WithPayloadSelector\x12\x10\n\x06\x65nable\x18\x01 \x01(\x08H\x00\x12\x31\n\x07include\x18\x02 \x01(\x0b\x32\x1e.qdrant.PayloadIncludeSelectorH\x00\x12\x31\n\x07\x65xclude\x18\x03 \x01(\x0b\x32\x1e.qdrant.PayloadExcludeSelectorH\x00\x42\x12\n\x10selector_options\"\x82\x01\n\x0cNamedVectors\x12\x32\n\x07vectors\x18\x01 \x03(\x0b\x32!.qdrant.NamedVectors.VectorsEntry\x1a>\n\x0cVectorsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.qdrant.Vector:\x02\x38\x01\"g\n\x07Vectors\x12 \n\x06vector\x18\x01 \x01(\x0b\x32\x0e.qdrant.VectorH\x00\x12\'\n\x07vectors\x18\x02 \x01(\x0b\x32\x14.qdrant.NamedVectorsH\x00\x42\x11\n\x0fvectors_options\" \n\x0fVectorsSelector\x12\r\n\x05names\x18\x01 \x03(\t\"g\n\x13WithVectorsSelector\x12\x10\n\x06\x65nable\x18\x01 \x01(\x08H\x00\x12*\n\x07include\x18\x02 \x01(\x0b\x32\x17.qdrant.VectorsSelectorH\x00\x42\x12\n\x10selector_options\"\x88\x01\n\x18QuantizationSearchParams\x12\x13\n\x06ignore\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x14\n\x07rescore\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12\x19\n\x0coversampling\x18\x03 \x01(\x01H\x02\x88\x01\x01\x42\t\n\x07_ignoreB\n\n\x08_rescoreB\x0f\n\r_oversampling\"\xc8\x01\n\x0cSearchParams\x12\x14\n\x07hnsw_ef\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x12\n\x05\x65xact\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12;\n\x0cquantization\x18\x03 \x01(\x0b\x32 .qdrant.QuantizationSearchParamsH\x02\x88\x01\x01\x12\x19\n\x0cindexed_only\x18\x04 \x01(\x08H\x03\x88\x01\x01\x42\n\n\x08_hnsw_efB\x08\n\x06_exactB\x0f\n\r_quantizationB\x0f\n\r_indexed_only\"\xd7\x03\n\x0cSearchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x0e\n\x06vector\x18\x02 \x03(\x02\x12\x1e\n\x06\x66ilter\x18\x03 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x04 \x01(\x04\x12\x31\n\x0cwith_payload\x18\x06 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x07 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\x08 \x01(\x02H\x00\x88\x01\x01\x12\x13\n\x06offset\x18\t \x01(\x04H\x01\x88\x01\x01\x12\x18\n\x0bvector_name\x18\n \x01(\tH\x02\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\x0b \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x03\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x0c \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x04\x88\x01\x01\x42\x12\n\x10_score_thresholdB\t\n\x07_offsetB\x0e\n\x0c_vector_nameB\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyJ\x04\x08\x05\x10\x06\"\xa6\x01\n\x11SearchBatchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12+\n\rsearch_points\x18\x02 \x03(\x0b\x32\x14.qdrant.SearchPoints\x12\x36\n\x10read_consistency\x18\x03 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x00\x88\x01\x01\x42\x13\n\x11_read_consistency\"\xb2\x01\n\nWithLookup\x12\x12\n\ncollection\x18\x01 \x01(\t\x12\x36\n\x0cwith_payload\x18\x02 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelectorH\x00\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\x03 \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x01\x88\x01\x01\x42\x0f\n\r_with_payloadB\x0f\n\r_with_vectors\"\x9a\x04\n\x11SearchPointGroups\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x0e\n\x06vector\x18\x02 \x03(\x02\x12\x1e\n\x06\x66ilter\x18\x03 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x04 \x01(\r\x12\x31\n\x0cwith_payload\x18\x05 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x06 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\x07 \x01(\x02H\x00\x88\x01\x01\x12\x18\n\x0bvector_name\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\t \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x02\x88\x01\x01\x12\x10\n\x08group_by\x18\n \x01(\t\x12\x12\n\ngroup_size\x18\x0b \x01(\r\x12\x36\n\x10read_consistency\x18\x0c \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x03\x88\x01\x01\x12,\n\x0bwith_lookup\x18\r \x01(\x0b\x32\x12.qdrant.WithLookupH\x04\x88\x01\x01\x42\x12\n\x10_score_thresholdB\x0e\n\x0c_vector_nameB\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyB\x0e\n\x0c_with_lookup\"\xe5\x02\n\x0cScrollPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x1e\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.Filter\x12$\n\x06offset\x18\x03 \x01(\x0b\x32\x0f.qdrant.PointIdH\x00\x88\x01\x01\x12\x12\n\x05limit\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x31\n\x0cwith_payload\x18\x06 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12\x36\n\x0cwith_vectors\x18\x07 \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x02\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x08 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x03\x88\x01\x01\x42\t\n\x07_offsetB\x08\n\x06_limitB\x0f\n\r_with_vectorsB\x13\n\x11_read_consistencyJ\x04\x08\x05\x10\x06\"S\n\x0eLookupLocation\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x18\n\x0bvector_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_vector_name\"\xd9\x05\n\x0fRecommendPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12!\n\x08positive\x18\x02 \x03(\x0b\x32\x0f.qdrant.PointId\x12!\n\x08negative\x18\x03 \x03(\x0b\x32\x0f.qdrant.PointId\x12\x1e\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x05 \x01(\x04\x12\x31\n\x0cwith_payload\x18\x07 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x08 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\t \x01(\x02H\x00\x88\x01\x01\x12\x13\n\x06offset\x18\n \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05using\x18\x0b \x01(\tH\x02\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\x0c \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x03\x88\x01\x01\x12\x30\n\x0blookup_from\x18\r \x01(\x0b\x32\x16.qdrant.LookupLocationH\x04\x88\x01\x01\x12\x36\n\x10read_consistency\x18\x0e \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x05\x88\x01\x01\x12\x30\n\x08strategy\x18\x10 \x01(\x0e\x32\x19.qdrant.RecommendStrategyH\x06\x88\x01\x01\x12(\n\x10positive_vectors\x18\x11 \x03(\x0b\x32\x0e.qdrant.Vector\x12(\n\x10negative_vectors\x18\x12 \x03(\x0b\x32\x0e.qdrant.VectorB\x12\n\x10_score_thresholdB\t\n\x07_offsetB\x08\n\x06_usingB\x0f\n\r_with_vectorsB\x0e\n\x0c_lookup_fromB\x13\n\x11_read_consistencyB\x0b\n\t_strategyJ\x04\x08\x06\x10\x07\"\xaf\x01\n\x14RecommendBatchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x31\n\x10recommend_points\x18\x02 \x03(\x0b\x32\x17.qdrant.RecommendPoints\x12\x36\n\x10read_consistency\x18\x03 \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x00\x88\x01\x01\x42\x13\n\x11_read_consistency\"\x9c\x06\n\x14RecommendPointGroups\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12!\n\x08positive\x18\x02 \x03(\x0b\x32\x0f.qdrant.PointId\x12!\n\x08negative\x18\x03 \x03(\x0b\x32\x0f.qdrant.PointId\x12\x1e\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x0e.qdrant.Filter\x12\r\n\x05limit\x18\x05 \x01(\r\x12\x31\n\x0cwith_payload\x18\x06 \x01(\x0b\x32\x1b.qdrant.WithPayloadSelector\x12$\n\x06params\x18\x07 \x01(\x0b\x32\x14.qdrant.SearchParams\x12\x1c\n\x0fscore_threshold\x18\x08 \x01(\x02H\x00\x88\x01\x01\x12\x12\n\x05using\x18\t \x01(\tH\x01\x88\x01\x01\x12\x36\n\x0cwith_vectors\x18\n \x01(\x0b\x32\x1b.qdrant.WithVectorsSelectorH\x02\x88\x01\x01\x12\x30\n\x0blookup_from\x18\x0b \x01(\x0b\x32\x16.qdrant.LookupLocationH\x03\x88\x01\x01\x12\x10\n\x08group_by\x18\x0c \x01(\t\x12\x12\n\ngroup_size\x18\r \x01(\r\x12\x36\n\x10read_consistency\x18\x0e \x01(\x0b\x32\x17.qdrant.ReadConsistencyH\x04\x88\x01\x01\x12,\n\x0bwith_lookup\x18\x0f \x01(\x0b\x32\x12.qdrant.WithLookupH\x05\x88\x01\x01\x12\x30\n\x08strategy\x18\x11 \x01(\x0e\x32\x19.qdrant.RecommendStrategyH\x06\x88\x01\x01\x12(\n\x10positive_vectors\x18\x12 \x03(\x0b\x32\x0e.qdrant.Vector\x12(\n\x10negative_vectors\x18\x13 \x03(\x0b\x32\x0e.qdrant.VectorB\x12\n\x10_score_thresholdB\x08\n\x06_usingB\x0f\n\r_with_vectorsB\x0e\n\x0c_lookup_fromB\x13\n\x11_read_consistencyB\x0e\n\x0c_with_lookupB\x0b\n\t_strategy\"d\n\x0b\x43ountPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x1e\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.Filter\x12\x12\n\x05\x65xact\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_exact\"\xc1\x08\n\x15PointsUpdateOperation\x12?\n\x06upsert\x18\x01 \x01(\x0b\x32-.qdrant.PointsUpdateOperation.PointStructListH\x00\x12(\n\x06\x64\x65lete\x18\x02 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x12?\n\x0bset_payload\x18\x03 \x01(\x0b\x32(.qdrant.PointsUpdateOperation.SetPayloadH\x00\x12\x45\n\x11overwrite_payload\x18\x04 \x01(\x0b\x32(.qdrant.PointsUpdateOperation.SetPayloadH\x00\x12\x45\n\x0e\x64\x65lete_payload\x18\x05 \x01(\x0b\x32+.qdrant.PointsUpdateOperation.DeletePayloadH\x00\x12/\n\rclear_payload\x18\x06 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x12\x45\n\x0eupdate_vectors\x18\x07 \x01(\x0b\x32+.qdrant.PointsUpdateOperation.UpdateVectorsH\x00\x12\x45\n\x0e\x64\x65lete_vectors\x18\x08 \x01(\x0b\x32+.qdrant.PointsUpdateOperation.DeleteVectorsH\x00\x1a\x36\n\x0fPointStructList\x12#\n\x06points\x18\x01 \x03(\x0b\x32\x13.qdrant.PointStruct\x1a\xdd\x01\n\nSetPayload\x12\x46\n\x07payload\x18\x01 \x03(\x0b\x32\x35.qdrant.PointsUpdateOperation.SetPayload.PayloadEntry\x12\x34\n\x0fpoints_selector\x18\x02 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\x12\n\x10_points_selector\x1ag\n\rDeletePayload\x12\x0c\n\x04keys\x18\x01 \x03(\t\x12\x34\n\x0fpoints_selector\x18\x02 \x01(\x0b\x32\x16.qdrant.PointsSelectorH\x00\x88\x01\x01\x42\x12\n\x10_points_selector\x1a\x35\n\rUpdateVectors\x12$\n\x06points\x18\x01 \x03(\x0b\x32\x14.qdrant.PointVectors\x1aj\n\rDeleteVectors\x12/\n\x0fpoints_selector\x18\x01 \x01(\x0b\x32\x16.qdrant.PointsSelector\x12(\n\x07vectors\x18\x02 \x01(\x0b\x32\x17.qdrant.VectorsSelectorB\x0b\n\toperation\"\xb6\x01\n\x11UpdateBatchPoints\x12\x17\n\x0f\x63ollection_name\x18\x01 \x01(\t\x12\x11\n\x04wait\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x31\n\noperations\x18\x03 \x03(\x0b\x32\x1d.qdrant.PointsUpdateOperation\x12,\n\x08ordering\x18\x04 \x01(\x0b\x32\x15.qdrant.WriteOrderingH\x01\x88\x01\x01\x42\x07\n\x05_waitB\x0b\n\t_ordering\"M\n\x17PointsOperationResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.qdrant.UpdateResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"J\n\x0cUpdateResult\x12\x14\n\x0coperation_id\x18\x01 \x01(\x04\x12$\n\x06status\x18\x02 \x01(\x0e\x32\x14.qdrant.UpdateStatus\"\xf5\x01\n\x0bScoredPoint\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12\x31\n\x07payload\x18\x02 \x03(\x0b\x32 .qdrant.ScoredPoint.PayloadEntry\x12\r\n\x05score\x18\x03 \x01(\x02\x12\x0f\n\x07version\x18\x05 \x01(\x04\x12%\n\x07vectors\x18\x06 \x01(\x0b\x32\x0f.qdrant.VectorsH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\n\n\x08_vectorsJ\x04\x08\x04\x10\x05\"\\\n\x07GroupId\x12\x18\n\x0eunsigned_value\x18\x01 \x01(\x04H\x00\x12\x17\n\rinteger_value\x18\x02 \x01(\x03H\x00\x12\x16\n\x0cstring_value\x18\x03 \x01(\tH\x00\x42\x06\n\x04kind\"t\n\nPointGroup\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.GroupId\x12!\n\x04hits\x18\x02 \x03(\x0b\x32\x13.qdrant.ScoredPoint\x12&\n\x06lookup\x18\x03 \x01(\x0b\x32\x16.qdrant.RetrievedPoint\"2\n\x0cGroupsResult\x12\"\n\x06groups\x18\x01 \x03(\x0b\x32\x12.qdrant.PointGroup\"C\n\x0eSearchResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.ScoredPoint\x12\x0c\n\x04time\x18\x02 \x01(\x01\"2\n\x0b\x42\x61tchResult\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.ScoredPoint\"H\n\x13SearchBatchResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.BatchResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"J\n\x14SearchGroupsResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.qdrant.GroupsResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"B\n\rCountResponse\x12#\n\x06result\x18\x01 \x01(\x0b\x32\x13.qdrant.CountResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"\x8b\x01\n\x0eScrollResponse\x12.\n\x10next_page_offset\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointIdH\x00\x88\x01\x01\x12&\n\x06result\x18\x02 \x03(\x0b\x32\x16.qdrant.RetrievedPoint\x12\x0c\n\x04time\x18\x03 \x01(\x01\x42\x13\n\x11_next_page_offset\"\x1c\n\x0b\x43ountResult\x12\r\n\x05\x63ount\x18\x01 \x01(\x04\"\xdb\x01\n\x0eRetrievedPoint\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12\x34\n\x07payload\x18\x02 \x03(\x0b\x32#.qdrant.RetrievedPoint.PayloadEntry\x12%\n\x07vectors\x18\x04 \x01(\x0b\x32\x0f.qdrant.VectorsH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\n\n\x08_vectorsJ\x04\x08\x03\x10\x04\"C\n\x0bGetResponse\x12&\n\x06result\x18\x01 \x03(\x0b\x32\x16.qdrant.RetrievedPoint\x12\x0c\n\x04time\x18\x02 \x01(\x01\"F\n\x11RecommendResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.ScoredPoint\x12\x0c\n\x04time\x18\x02 \x01(\x01\"K\n\x16RecommendBatchResponse\x12#\n\x06result\x18\x01 \x03(\x0b\x32\x13.qdrant.BatchResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"M\n\x17RecommendGroupsResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.qdrant.GroupsResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"I\n\x13UpdateBatchResponse\x12$\n\x06result\x18\x01 \x03(\x0b\x32\x14.qdrant.UpdateResult\x12\x0c\n\x04time\x18\x02 \x01(\x01\"q\n\x06\x46ilter\x12!\n\x06should\x18\x01 \x03(\x0b\x32\x11.qdrant.Condition\x12\x1f\n\x04must\x18\x02 \x03(\x0b\x32\x11.qdrant.Condition\x12#\n\x08must_not\x18\x03 \x03(\x0b\x32\x11.qdrant.Condition\"\x99\x02\n\tCondition\x12\'\n\x05\x66ield\x18\x01 \x01(\x0b\x32\x16.qdrant.FieldConditionH\x00\x12,\n\x08is_empty\x18\x02 \x01(\x0b\x32\x18.qdrant.IsEmptyConditionH\x00\x12(\n\x06has_id\x18\x03 \x01(\x0b\x32\x16.qdrant.HasIdConditionH\x00\x12 \n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x0e.qdrant.FilterH\x00\x12*\n\x07is_null\x18\x05 \x01(\x0b\x32\x17.qdrant.IsNullConditionH\x00\x12)\n\x06nested\x18\x06 \x01(\x0b\x32\x17.qdrant.NestedConditionH\x00\x42\x12\n\x10\x63ondition_one_of\"\x1f\n\x10IsEmptyCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\"\x1e\n\x0fIsNullCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\"1\n\x0eHasIdCondition\x12\x1f\n\x06has_id\x18\x01 \x03(\x0b\x32\x0f.qdrant.PointId\">\n\x0fNestedCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1e\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.Filter\"\xdd\x01\n\x0e\x46ieldCondition\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05match\x18\x02 \x01(\x0b\x32\r.qdrant.Match\x12\x1c\n\x05range\x18\x03 \x01(\x0b\x32\r.qdrant.Range\x12\x30\n\x10geo_bounding_box\x18\x04 \x01(\x0b\x32\x16.qdrant.GeoBoundingBox\x12%\n\ngeo_radius\x18\x05 \x01(\x0b\x32\x11.qdrant.GeoRadius\x12)\n\x0cvalues_count\x18\x06 \x01(\x0b\x32\x13.qdrant.ValuesCount\"\xa3\x02\n\x05Match\x12\x11\n\x07keyword\x18\x01 \x01(\tH\x00\x12\x11\n\x07integer\x18\x02 \x01(\x03H\x00\x12\x11\n\x07\x62oolean\x18\x03 \x01(\x08H\x00\x12\x0e\n\x04text\x18\x04 \x01(\tH\x00\x12+\n\x08keywords\x18\x05 \x01(\x0b\x32\x17.qdrant.RepeatedStringsH\x00\x12,\n\x08integers\x18\x06 \x01(\x0b\x32\x18.qdrant.RepeatedIntegersH\x00\x12\x33\n\x0f\x65xcept_integers\x18\x07 \x01(\x0b\x32\x18.qdrant.RepeatedIntegersH\x00\x12\x32\n\x0f\x65xcept_keywords\x18\x08 \x01(\x0b\x32\x17.qdrant.RepeatedStringsH\x00\x42\r\n\x0bmatch_value\"\"\n\x0fRepeatedStrings\x12\x0f\n\x07strings\x18\x01 \x03(\t\"$\n\x10RepeatedIntegers\x12\x10\n\x08integers\x18\x01 \x03(\x03\"k\n\x05Range\x12\x0f\n\x02lt\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x0f\n\x02gt\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x10\n\x03gte\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x10\n\x03lte\x18\x04 \x01(\x01H\x03\x88\x01\x01\x42\x05\n\x03_ltB\x05\n\x03_gtB\x06\n\x04_gteB\x06\n\x04_lte\"\\\n\x0eGeoBoundingBox\x12\"\n\x08top_left\x18\x01 \x01(\x0b\x32\x10.qdrant.GeoPoint\x12&\n\x0c\x62ottom_right\x18\x02 \x01(\x0b\x32\x10.qdrant.GeoPoint\"=\n\tGeoRadius\x12 \n\x06\x63\x65nter\x18\x01 \x01(\x0b\x32\x10.qdrant.GeoPoint\x12\x0e\n\x06radius\x18\x02 \x01(\x02\".\n\nGeoPolygon\x12 \n\x06points\x18\x01 \x03(\x0b\x32\x10.qdrant.GeoPoint\"q\n\x0bValuesCount\x12\x0f\n\x02lt\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x0f\n\x02gt\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x10\n\x03gte\x18\x03 \x01(\x04H\x02\x88\x01\x01\x12\x10\n\x03lte\x18\x04 \x01(\x04H\x03\x88\x01\x01\x42\x05\n\x03_ltB\x05\n\x03_gtB\x06\n\x04_gteB\x06\n\x04_lte\"u\n\x0ePointsSelector\x12\'\n\x06points\x18\x01 \x01(\x0b\x32\x15.qdrant.PointsIdsListH\x00\x12 \n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0e.qdrant.FilterH\x00\x42\x18\n\x16points_selector_one_of\"-\n\rPointsIdsList\x12\x1c\n\x03ids\x18\x01 \x03(\x0b\x32\x0f.qdrant.PointId\"\xd5\x01\n\x0bPointStruct\x12\x1b\n\x02id\x18\x01 \x01(\x0b\x32\x0f.qdrant.PointId\x12\x31\n\x07payload\x18\x03 \x03(\x0b\x32 .qdrant.PointStruct.PayloadEntry\x12%\n\x07vectors\x18\x04 \x01(\x0b\x32\x0f.qdrant.VectorsH\x00\x88\x01\x01\x1a=\n\x0cPayloadEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.qdrant.Value:\x02\x38\x01\x42\n\n\x08_vectorsJ\x04\x08\x02\x10\x03\"$\n\x08GeoPoint\x12\x0b\n\x03lon\x18\x01 \x01(\x01\x12\x0b\n\x03lat\x18\x02 \x01(\x01*5\n\x11WriteOrderingType\x12\x08\n\x04Weak\x10\x00\x12\n\n\x06Medium\x10\x01\x12\n\n\x06Strong\x10\x02*8\n\x13ReadConsistencyType\x12\x07\n\x03\x41ll\x10\x00\x12\x0c\n\x08Majority\x10\x01\x12\n\n\x06Quorum\x10\x02*\x83\x01\n\tFieldType\x12\x14\n\x10\x46ieldTypeKeyword\x10\x00\x12\x14\n\x10\x46ieldTypeInteger\x10\x01\x12\x12\n\x0e\x46ieldTypeFloat\x10\x02\x12\x10\n\x0c\x46ieldTypeGeo\x10\x03\x12\x11\n\rFieldTypeText\x10\x04\x12\x11\n\rFieldTypeBool\x10\x05*5\n\x11RecommendStrategy\x12\x11\n\rAverageVector\x10\x00\x12\r\n\tBestScore\x10\x01*H\n\x0cUpdateStatus\x12\x17\n\x13UnknownUpdateStatus\x10\x00\x12\x10\n\x0c\x41\x63knowledged\x10\x01\x12\r\n\tCompleted\x10\x02\x62\x06proto3') _WRITEORDERINGTYPE = DESCRIPTOR.enum_types_by_name['WriteOrderingType'] WriteOrderingType = enum_type_wrapper.EnumTypeWrapper(_WRITEORDERINGTYPE) @@ -25,6 +25,8 @@ ReadConsistencyType = enum_type_wrapper.EnumTypeWrapper(_READCONSISTENCYTYPE) _FIELDTYPE = DESCRIPTOR.enum_types_by_name['FieldType'] FieldType = enum_type_wrapper.EnumTypeWrapper(_FIELDTYPE) +_RECOMMENDSTRATEGY = DESCRIPTOR.enum_types_by_name['RecommendStrategy'] +RecommendStrategy = enum_type_wrapper.EnumTypeWrapper(_RECOMMENDSTRATEGY) _UPDATESTATUS = DESCRIPTOR.enum_types_by_name['UpdateStatus'] UpdateStatus = enum_type_wrapper.EnumTypeWrapper(_UPDATESTATUS) Weak = 0 @@ -39,6 +41,8 @@ FieldTypeGeo = 3 FieldTypeText = 4 FieldTypeBool = 5 +AverageVector = 0 +BestScore = 1 UnknownUpdateStatus = 0 Acknowledged = 1 Completed = 2 @@ -750,14 +754,16 @@ _RETRIEVEDPOINT_PAYLOADENTRY._serialized_options = b'8\001' _POINTSTRUCT_PAYLOADENTRY._options = None _POINTSTRUCT_PAYLOADENTRY._serialized_options = b'8\001' - _WRITEORDERINGTYPE._serialized_start=11957 - _WRITEORDERINGTYPE._serialized_end=12010 - _READCONSISTENCYTYPE._serialized_start=12012 - _READCONSISTENCYTYPE._serialized_end=12068 - _FIELDTYPE._serialized_start=12071 - _FIELDTYPE._serialized_end=12202 - _UPDATESTATUS._serialized_start=12204 - _UPDATESTATUS._serialized_end=12276 + _WRITEORDERINGTYPE._serialized_start=12251 + _WRITEORDERINGTYPE._serialized_end=12304 + _READCONSISTENCYTYPE._serialized_start=12306 + _READCONSISTENCYTYPE._serialized_end=12362 + _FIELDTYPE._serialized_start=12365 + _FIELDTYPE._serialized_end=12496 + _RECOMMENDSTRATEGY._serialized_start=12498 + _RECOMMENDSTRATEGY._serialized_end=12551 + _UPDATESTATUS._serialized_start=12553 + _UPDATESTATUS._serialized_end=12625 _WRITEORDERING._serialized_start=64 _WRITEORDERING._serialized_end=120 _READCONSISTENCY._serialized_start=122 @@ -823,109 +829,109 @@ _LOOKUPLOCATION._serialized_start=5286 _LOOKUPLOCATION._serialized_end=5369 _RECOMMENDPOINTS._serialized_start=5372 - _RECOMMENDPOINTS._serialized_end=5954 - _RECOMMENDBATCHPOINTS._serialized_start=5957 - _RECOMMENDBATCHPOINTS._serialized_end=6132 - _RECOMMENDPOINTGROUPS._serialized_start=6135 - _RECOMMENDPOINTGROUPS._serialized_end=6784 - _COUNTPOINTS._serialized_start=6786 - _COUNTPOINTS._serialized_end=6886 - _POINTSUPDATEOPERATION._serialized_start=6889 - _POINTSUPDATEOPERATION._serialized_end=7978 - _POINTSUPDATEOPERATION_POINTSTRUCTLIST._serialized_start=7419 - _POINTSUPDATEOPERATION_POINTSTRUCTLIST._serialized_end=7473 - _POINTSUPDATEOPERATION_SETPAYLOAD._serialized_start=7476 - _POINTSUPDATEOPERATION_SETPAYLOAD._serialized_end=7697 + _RECOMMENDPOINTS._serialized_end=6101 + _RECOMMENDBATCHPOINTS._serialized_start=6104 + _RECOMMENDBATCHPOINTS._serialized_end=6279 + _RECOMMENDPOINTGROUPS._serialized_start=6282 + _RECOMMENDPOINTGROUPS._serialized_end=7078 + _COUNTPOINTS._serialized_start=7080 + _COUNTPOINTS._serialized_end=7180 + _POINTSUPDATEOPERATION._serialized_start=7183 + _POINTSUPDATEOPERATION._serialized_end=8272 + _POINTSUPDATEOPERATION_POINTSTRUCTLIST._serialized_start=7713 + _POINTSUPDATEOPERATION_POINTSTRUCTLIST._serialized_end=7767 + _POINTSUPDATEOPERATION_SETPAYLOAD._serialized_start=7770 + _POINTSUPDATEOPERATION_SETPAYLOAD._serialized_end=7991 _POINTSUPDATEOPERATION_SETPAYLOAD_PAYLOADENTRY._serialized_start=1609 _POINTSUPDATEOPERATION_SETPAYLOAD_PAYLOADENTRY._serialized_end=1670 - _POINTSUPDATEOPERATION_DELETEPAYLOAD._serialized_start=7699 - _POINTSUPDATEOPERATION_DELETEPAYLOAD._serialized_end=7802 - _POINTSUPDATEOPERATION_UPDATEVECTORS._serialized_start=7804 - _POINTSUPDATEOPERATION_UPDATEVECTORS._serialized_end=7857 - _POINTSUPDATEOPERATION_DELETEVECTORS._serialized_start=7859 - _POINTSUPDATEOPERATION_DELETEVECTORS._serialized_end=7965 - _UPDATEBATCHPOINTS._serialized_start=7981 - _UPDATEBATCHPOINTS._serialized_end=8163 - _POINTSOPERATIONRESPONSE._serialized_start=8165 - _POINTSOPERATIONRESPONSE._serialized_end=8242 - _UPDATERESULT._serialized_start=8244 - _UPDATERESULT._serialized_end=8318 - _SCOREDPOINT._serialized_start=8321 - _SCOREDPOINT._serialized_end=8566 + _POINTSUPDATEOPERATION_DELETEPAYLOAD._serialized_start=7993 + _POINTSUPDATEOPERATION_DELETEPAYLOAD._serialized_end=8096 + _POINTSUPDATEOPERATION_UPDATEVECTORS._serialized_start=8098 + _POINTSUPDATEOPERATION_UPDATEVECTORS._serialized_end=8151 + _POINTSUPDATEOPERATION_DELETEVECTORS._serialized_start=8153 + _POINTSUPDATEOPERATION_DELETEVECTORS._serialized_end=8259 + _UPDATEBATCHPOINTS._serialized_start=8275 + _UPDATEBATCHPOINTS._serialized_end=8457 + _POINTSOPERATIONRESPONSE._serialized_start=8459 + _POINTSOPERATIONRESPONSE._serialized_end=8536 + _UPDATERESULT._serialized_start=8538 + _UPDATERESULT._serialized_end=8612 + _SCOREDPOINT._serialized_start=8615 + _SCOREDPOINT._serialized_end=8860 _SCOREDPOINT_PAYLOADENTRY._serialized_start=1609 _SCOREDPOINT_PAYLOADENTRY._serialized_end=1670 - _GROUPID._serialized_start=8568 - _GROUPID._serialized_end=8660 - _POINTGROUP._serialized_start=8662 - _POINTGROUP._serialized_end=8778 - _GROUPSRESULT._serialized_start=8780 - _GROUPSRESULT._serialized_end=8830 - _SEARCHRESPONSE._serialized_start=8832 - _SEARCHRESPONSE._serialized_end=8899 - _BATCHRESULT._serialized_start=8901 - _BATCHRESULT._serialized_end=8951 - _SEARCHBATCHRESPONSE._serialized_start=8953 - _SEARCHBATCHRESPONSE._serialized_end=9025 - _SEARCHGROUPSRESPONSE._serialized_start=9027 - _SEARCHGROUPSRESPONSE._serialized_end=9101 - _COUNTRESPONSE._serialized_start=9103 - _COUNTRESPONSE._serialized_end=9169 - _SCROLLRESPONSE._serialized_start=9172 - _SCROLLRESPONSE._serialized_end=9311 - _COUNTRESULT._serialized_start=9313 - _COUNTRESULT._serialized_end=9341 - _RETRIEVEDPOINT._serialized_start=9344 - _RETRIEVEDPOINT._serialized_end=9563 + _GROUPID._serialized_start=8862 + _GROUPID._serialized_end=8954 + _POINTGROUP._serialized_start=8956 + _POINTGROUP._serialized_end=9072 + _GROUPSRESULT._serialized_start=9074 + _GROUPSRESULT._serialized_end=9124 + _SEARCHRESPONSE._serialized_start=9126 + _SEARCHRESPONSE._serialized_end=9193 + _BATCHRESULT._serialized_start=9195 + _BATCHRESULT._serialized_end=9245 + _SEARCHBATCHRESPONSE._serialized_start=9247 + _SEARCHBATCHRESPONSE._serialized_end=9319 + _SEARCHGROUPSRESPONSE._serialized_start=9321 + _SEARCHGROUPSRESPONSE._serialized_end=9395 + _COUNTRESPONSE._serialized_start=9397 + _COUNTRESPONSE._serialized_end=9463 + _SCROLLRESPONSE._serialized_start=9466 + _SCROLLRESPONSE._serialized_end=9605 + _COUNTRESULT._serialized_start=9607 + _COUNTRESULT._serialized_end=9635 + _RETRIEVEDPOINT._serialized_start=9638 + _RETRIEVEDPOINT._serialized_end=9857 _RETRIEVEDPOINT_PAYLOADENTRY._serialized_start=1609 _RETRIEVEDPOINT_PAYLOADENTRY._serialized_end=1670 - _GETRESPONSE._serialized_start=9565 - _GETRESPONSE._serialized_end=9632 - _RECOMMENDRESPONSE._serialized_start=9634 - _RECOMMENDRESPONSE._serialized_end=9704 - _RECOMMENDBATCHRESPONSE._serialized_start=9706 - _RECOMMENDBATCHRESPONSE._serialized_end=9781 - _RECOMMENDGROUPSRESPONSE._serialized_start=9783 - _RECOMMENDGROUPSRESPONSE._serialized_end=9860 - _UPDATEBATCHRESPONSE._serialized_start=9862 - _UPDATEBATCHRESPONSE._serialized_end=9935 - _FILTER._serialized_start=9937 - _FILTER._serialized_end=10050 - _CONDITION._serialized_start=10053 - _CONDITION._serialized_end=10334 - _ISEMPTYCONDITION._serialized_start=10336 - _ISEMPTYCONDITION._serialized_end=10367 - _ISNULLCONDITION._serialized_start=10369 - _ISNULLCONDITION._serialized_end=10399 - _HASIDCONDITION._serialized_start=10401 - _HASIDCONDITION._serialized_end=10450 - _NESTEDCONDITION._serialized_start=10452 - _NESTEDCONDITION._serialized_end=10514 - _FIELDCONDITION._serialized_start=10517 - _FIELDCONDITION._serialized_end=10738 - _MATCH._serialized_start=10741 - _MATCH._serialized_end=11032 - _REPEATEDSTRINGS._serialized_start=11034 - _REPEATEDSTRINGS._serialized_end=11068 - _REPEATEDINTEGERS._serialized_start=11070 - _REPEATEDINTEGERS._serialized_end=11106 - _RANGE._serialized_start=11108 - _RANGE._serialized_end=11215 - _GEOBOUNDINGBOX._serialized_start=11217 - _GEOBOUNDINGBOX._serialized_end=11309 - _GEORADIUS._serialized_start=11311 - _GEORADIUS._serialized_end=11372 - _GEOPOLYGON._serialized_start=11374 - _GEOPOLYGON._serialized_end=11420 - _VALUESCOUNT._serialized_start=11422 - _VALUESCOUNT._serialized_end=11535 - _POINTSSELECTOR._serialized_start=11537 - _POINTSSELECTOR._serialized_end=11654 - _POINTSIDSLIST._serialized_start=11656 - _POINTSIDSLIST._serialized_end=11701 - _POINTSTRUCT._serialized_start=11704 - _POINTSTRUCT._serialized_end=11917 + _GETRESPONSE._serialized_start=9859 + _GETRESPONSE._serialized_end=9926 + _RECOMMENDRESPONSE._serialized_start=9928 + _RECOMMENDRESPONSE._serialized_end=9998 + _RECOMMENDBATCHRESPONSE._serialized_start=10000 + _RECOMMENDBATCHRESPONSE._serialized_end=10075 + _RECOMMENDGROUPSRESPONSE._serialized_start=10077 + _RECOMMENDGROUPSRESPONSE._serialized_end=10154 + _UPDATEBATCHRESPONSE._serialized_start=10156 + _UPDATEBATCHRESPONSE._serialized_end=10229 + _FILTER._serialized_start=10231 + _FILTER._serialized_end=10344 + _CONDITION._serialized_start=10347 + _CONDITION._serialized_end=10628 + _ISEMPTYCONDITION._serialized_start=10630 + _ISEMPTYCONDITION._serialized_end=10661 + _ISNULLCONDITION._serialized_start=10663 + _ISNULLCONDITION._serialized_end=10693 + _HASIDCONDITION._serialized_start=10695 + _HASIDCONDITION._serialized_end=10744 + _NESTEDCONDITION._serialized_start=10746 + _NESTEDCONDITION._serialized_end=10808 + _FIELDCONDITION._serialized_start=10811 + _FIELDCONDITION._serialized_end=11032 + _MATCH._serialized_start=11035 + _MATCH._serialized_end=11326 + _REPEATEDSTRINGS._serialized_start=11328 + _REPEATEDSTRINGS._serialized_end=11362 + _REPEATEDINTEGERS._serialized_start=11364 + _REPEATEDINTEGERS._serialized_end=11400 + _RANGE._serialized_start=11402 + _RANGE._serialized_end=11509 + _GEOBOUNDINGBOX._serialized_start=11511 + _GEOBOUNDINGBOX._serialized_end=11603 + _GEORADIUS._serialized_start=11605 + _GEORADIUS._serialized_end=11666 + _GEOPOLYGON._serialized_start=11668 + _GEOPOLYGON._serialized_end=11714 + _VALUESCOUNT._serialized_start=11716 + _VALUESCOUNT._serialized_end=11829 + _POINTSSELECTOR._serialized_start=11831 + _POINTSSELECTOR._serialized_end=11948 + _POINTSIDSLIST._serialized_start=11950 + _POINTSIDSLIST._serialized_end=11995 + _POINTSTRUCT._serialized_start=11998 + _POINTSTRUCT._serialized_end=12211 _POINTSTRUCT_PAYLOADENTRY._serialized_start=1609 _POINTSTRUCT_PAYLOADENTRY._serialized_end=1670 - _GEOPOINT._serialized_start=11919 - _GEOPOINT._serialized_end=11955 + _GEOPOINT._serialized_start=12213 + _GEOPOINT._serialized_end=12249 # @@protoc_insertion_point(module_scope) diff --git a/qdrant_client/grpc/points_service_pb2_grpc.py b/qdrant_client/grpc/points_service_pb2_grpc.py index 552b0631..630fe5c3 100644 --- a/qdrant_client/grpc/points_service_pb2_grpc.py +++ b/qdrant_client/grpc/points_service_pb2_grpc.py @@ -233,7 +233,7 @@ def SearchGroups(self, request, context): def Scroll(self, request, context): """ - Iterate over all or filtered points points + Iterate over all or filtered points """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') diff --git a/qdrant_client/http/models/models.py b/qdrant_client/http/models/models.py index 9fc1292b..d50f1cd2 100644 --- a/qdrant_client/http/models/models.py +++ b/qdrant_client/http/models/models.py @@ -168,6 +168,10 @@ class CollectionParams(BaseModel, extra="forbid"): default=1, description="Defines how many replicas should apply the operation for us to consider it successful. Increasing this number will make the collection more resilient to inconsistencies, but will also make it fail if not enough replicas are available. Does not have any performance impact.", ) + read_fan_out_factor: Optional[int] = Field( + default=None, + description="Defines how many additional replicas should be processing read request at the same time. Default value is Auto, which means that fan-out will be determined automatically based on the busyness of the local replica. Having more than 0 might be useful to smooth latency spikes of individual nodes.", + ) on_disk_payload: Optional[bool] = Field( default=False, description="If true - point's payload will not be stored in memory. It will be read from the disk every time it is requested. This setting saves RAM by (slightly) increasing the response time. Note: those payload values that are involved in filtering and are indexed - remain in RAM.", @@ -179,6 +183,10 @@ class CollectionParamsDiff(BaseModel, extra="forbid"): write_consistency_factor: Optional[int] = Field( default=None, description="Minimal number successful responses from replicas to consider operation successful" ) + read_fan_out_factor: Optional[int] = Field( + default=None, + description="Fan-out every read request to these many additional remote nodes (and return first available response)", + ) on_disk_payload: Optional[bool] = Field( default=None, description="If true - point's payload will not be stored in memory. It will be read from the disk every time it is requested. This setting saves RAM by (slightly) increasing the response time. Note: those payload values that are involved in filtering and are indexed - remain in RAM.", @@ -186,6 +194,10 @@ class CollectionParamsDiff(BaseModel, extra="forbid"): class CollectionStatus(str, Enum): + """ + Current state of the collection. `Green` - all good. `Yellow` - optimization is running, `Red` - some operations failed and was not recovered + """ + GREEN = "green" YELLOW = "yellow" RED = "red" @@ -385,6 +397,10 @@ class Disabled(str, Enum): class Distance(str, Enum): + """ + Type of internal tags, build from payload Distance function types used to compare vectors + """ + COSINE = "Cosine" EUCLID = "Euclid" DOT = "Dot" @@ -933,6 +949,10 @@ class OptimizersConfigDiff(BaseModel, extra="forbid"): class OptimizersStatusOneOf(str, Enum): + """ + Optimizers are reporting as expected + """ + OK = "ok" @@ -980,6 +1000,10 @@ class PayloadIndexTelemetry(BaseModel, extra="forbid"): class PayloadSchemaType(str, Enum): + """ + All possible names of payload types + """ + KEYWORD = "keyword" INTEGER = "integer" FLOAT = "float" @@ -1071,8 +1095,8 @@ class QuantizationSearchParams(BaseModel, extra="forbid"): default=False, description="If true, quantized vectors are ignored. Default is false." ) rescore: Optional[bool] = Field( - default=False, - description="If true, use original vectors to re-score top-k results. Might require more time in case if original vectors are stored on disk. Default is false.", + default=None, + description="If true, use original vectors to re-score top-k results. Might require more time in case if original vectors are stored on disk. If not set, qdrant decides automatically apply rescoring or not.", ) oversampling: Optional[float] = Field( default=None, @@ -1112,14 +1136,21 @@ class Range(BaseModel, extra="forbid"): class ReadConsistencyType(str, Enum): + """ + * `majority` - send N/2+1 random request and return points, which present on all of them * `quorum` - send requests to all nodes and return points which present on majority of nodes * `all` - send requests to all nodes and return points which present on all nodes + """ + MAJORITY = "majority" QUORUM = "quorum" ALL = "all" class RecommendGroupsRequest(BaseModel, extra="forbid"): - positive: List["ExtendedPointId"] = Field(..., description="Look for vectors closest to those") - negative: Optional[List["ExtendedPointId"]] = Field(default=[], description="Try to avoid vectors like this") + positive: Optional[List["RecommendExample"]] = Field(default=[], description="Look for vectors closest to those") + negative: Optional[List["RecommendExample"]] = Field(default=[], description="Try to avoid vectors like this") + strategy: Optional["RecommendStrategy"] = Field( + default=None, description="How to use positive and negative examples to find the results" + ) filter: Optional["Filter"] = Field(default=None, description="Look only for points which satisfies this conditions") params: Optional["SearchParams"] = Field(default=None, description="Additional search params") with_payload: Optional["WithPayloadInterface"] = Field( @@ -1153,11 +1184,14 @@ class RecommendGroupsRequest(BaseModel, extra="forbid"): class RecommendRequest(BaseModel, extra="forbid"): """ - Recommendation request. Provides positive and negative examples of the vectors, which are already stored in the collection. Service should look for the points which are closer to positive examples and at the same time further to negative examples. The concrete way of how to compare negative and positive distances is up to implementation in `segment` crate. + Recommendation request. Provides positive and negative examples of the vectors, which can be ids of points that are already stored in the collection, raw vectors, or even ids and vectors combined. Service should look for the points which are closer to positive examples and at the same time further to negative examples. The concrete way of how to compare negative and positive distances is up to the `strategy` chosen. """ - positive: List["ExtendedPointId"] = Field(..., description="Look for vectors closest to those") - negative: Optional[List["ExtendedPointId"]] = Field(default=[], description="Try to avoid vectors like this") + positive: Optional[List["RecommendExample"]] = Field(default=[], description="Look for vectors closest to those") + negative: Optional[List["RecommendExample"]] = Field(default=[], description="Try to avoid vectors like this") + strategy: Optional["RecommendStrategy"] = Field( + default=None, description="How to use positive and negative examples to find the results" + ) filter: Optional["Filter"] = Field(default=None, description="Look only for points which satisfies this conditions") params: Optional["SearchParams"] = Field(default=None, description="Additional search params") limit: int = Field(..., description="Max number of result to return") @@ -1189,6 +1223,15 @@ class RecommendRequestBatch(BaseModel, extra="forbid"): searches: List["RecommendRequest"] = Field(..., description="") +class RecommendStrategy(str, Enum): + """ + How to use positive and negative examples to find the results, default is `average_vector`: * `average_vector` - Average positive and negative vectors and create a single query with the formula `query = avg_pos + avg_pos - avg_neg`. Then performs normal search. * `best_score` - Uses custom search objective. Each candidate is compared against all examples, its score is then chosen from the `max(max_pos_score, max_neg_score)`. If the `max_neg_score` is chosen then it is squared and negated, otherwise it is just the `max_pos_score`. + """ + + AVERAGE_VECTOR = "average_vector" + BEST_SCORE = "best_score" + + class Record(BaseModel, extra="forbid"): """ Point data @@ -1242,6 +1285,10 @@ class ReplicaSetTelemetry(BaseModel, extra="forbid"): class ReplicaState(str, Enum): + """ + State of the single shard within a replica set. + """ + ACTIVE = "Active" DEAD = "Dead" PARTIAL = "Partial" @@ -1436,6 +1483,10 @@ class SegmentTelemetry(BaseModel, extra="forbid"): class SegmentType(str, Enum): + """ + Type of segment + """ + PLAIN = "plain" INDEXED = "indexed" SPECIAL = "special" @@ -1477,6 +1528,10 @@ class SnapshotDescription(BaseModel, extra="forbid"): class SnapshotPriority(str, Enum): + """ + Defines source of truth for snapshot recovery: `NoSync` means - restore snapshot without *any* additional synchronization. `Snapshot` means - prefer snapshot data over the current state. `Replica` means - prefer existing data over the snapshot. + """ + NO_SYNC = "no_sync" SNAPSHOT = "snapshot" REPLICA = "replica" @@ -1494,6 +1549,10 @@ class SnapshotRecover(BaseModel, extra="forbid"): class StateRole(str, Enum): + """ + Role of the peer in the consensus + """ + FOLLOWER = "Follower" CANDIDATE = "Candidate" LEADER = "Leader" @@ -1586,6 +1645,10 @@ class UpdateResult(BaseModel, extra="forbid"): class UpdateStatus(str, Enum): + """ + `Acknowledged` - Request is saved to WAL and will be process in a queue. `Completed` - Request is completed, changes are actual. + """ + ACKNOWLEDGED = "acknowledged" COMPLETED = "completed" @@ -1678,14 +1741,26 @@ class VectorParamsDiff(BaseModel, extra="forbid"): class VectorStorageTypeOneOf(str, Enum): + """ + Storage in memory (RAM) Will be very fast at the cost of consuming a lot of memory. + """ + MEMORY = "Memory" class VectorStorageTypeOneOf1(str, Enum): + """ + Storage in mmap file, not appendable Search performance is defined by disk speed and the fraction of vectors that fit in memory. + """ + MMAP = "Mmap" class VectorStorageTypeOneOf2(str, Enum): + """ + Storage in chunked mmap files, appendable Search performance is defined by disk speed and the fraction of vectors that fit in memory. + """ + CHUNKEDMMAP = "ChunkedMmap" @@ -1716,6 +1791,10 @@ class WithLookup(BaseModel, extra="forbid"): class WriteOrdering(str, Enum): + """ + Defines write ordering guarantees for collection operations * `weak` - write operations may be reordered, works faster, default * `medium` - write operations go through dynamically selected leader, may be inconsistent for a short period of time in case of leader change * `strong` - Write operations go through the permanent leader, consistent, but may be unavailable if leader is down + """ + WEAK = "weak" MEDIUM = "medium" STRONG = "strong" @@ -1872,6 +1951,10 @@ class WriteOrdering(str, Enum): PayloadSchemaType, PayloadSchemaParams, ] +RecommendExample = Union[ + ExtendedPointId, + List[StrictFloat], +] WithPayloadInterface = Union[ PayloadSelector, List[StrictStr], diff --git a/qdrant_client/local/distances.py b/qdrant_client/local/distances.py index 713b4a69..c8beb993 100644 --- a/qdrant_client/local/distances.py +++ b/qdrant_client/local/distances.py @@ -1,10 +1,26 @@ from enum import Enum +from typing import List, Optional, Union +from qdrant_client.conversions import common_types as types import numpy as np from qdrant_client.http import models +class RecoQuery: + def __init__( + self, + positive: Optional[List[List[float]]] = None, + negative: Optional[List[List[float]]] = None + ): + positive = positive if positive is not None else [] + negative = negative if negative is not None else [] + self.positive: List[types.NumpyArray] = [np.array(vector) for vector in positive] + self.negative: List[types.NumpyArray] = [np.array(vector) for vector in negative] + + +QueryVector = Union[RecoQuery, types.NumpyArray] + class DistanceOrder(str, Enum): BIGGER_IS_BETTER = "bigger_is_better" SMALLER_IS_BETTER = "smaller_is_better" @@ -64,7 +80,7 @@ def euclidean_distance(query: np.ndarray, vectors: np.ndarray) -> np.ndarray: def calculate_distance( query: np.ndarray, vectors: np.ndarray, distance_type: models.Distance -) -> np.ndarray: +) -> types.NumpyArray: if distance_type == models.Distance.COSINE: return cosine_similarity(query, vectors) elif distance_type == models.Distance.DOT: @@ -75,6 +91,43 @@ def calculate_distance( raise ValueError(f"Unknown distance type {distance_type}") +def calculate_best_scores( + query: RecoQuery, vectors: np.ndarray, distance_type: models.Distance +) -> types.NumpyArray: + + def get_best_scores(examples: List[types.NumpyArray]) -> types.NumpyArray: + vector_count = vectors.shape[0] + + # Get scores to all examples + scores: List[types.NumpyArray] = [] + for example in examples: + score = calculate_distance(example, vectors, distance_type) + scores.append(score) + + # Keep only max (or min) for each vector + if distance_to_order(distance_type) == DistanceOrder.BIGGER_IS_BETTER: + if len(scores) == 0: + scores.append(np.full(vector_count, -np.inf)) + best_scores = np.array(scores, dtype=np.float32).max(axis=0) + else: + if len(scores) == 0: + scores.append(np.full(vector_count, np.inf)) + best_scores = np.array(scores, dtype=np.float32).min(axis=0) + + return best_scores + + pos = get_best_scores(query.positive) + neg = get_best_scores(query.negative) + + # Choose from best positive or best negative, + # in case of choosing best negative, square and negate it to make it smaller than any positive + if distance_to_order(distance_type) == DistanceOrder.BIGGER_IS_BETTER: + return np.where(pos > neg, pos, -(neg*neg)) + else: + # neg*neg is not negated here because of the DistanceOrder.SMALLER_IS_BETTER + return np.where(pos < neg, pos, neg*neg) + + def test_distances() -> None: query = np.array([1.0, 2.0, 3.0]) vectors = np.array([[1.0, 2.0, 3.0], [1.0, 2.0, 3.0]]) diff --git a/qdrant_client/local/local_collection.py b/qdrant_client/local/local_collection.py index a6553b42..a070dae5 100644 --- a/qdrant_client/local/local_collection.py +++ b/qdrant_client/local/local_collection.py @@ -1,14 +1,18 @@ import uuid from collections import OrderedDict, defaultdict -from typing import Dict, List, Optional, Sequence, Tuple, Union +from typing import Dict, List, Optional, Sequence, Tuple, Union, get_args import numpy as np from qdrant_client._pydantic_compat import construct from qdrant_client.conversions import common_types as types from qdrant_client.http import models +from qdrant_client.http.models.models import ExtendedPointId from qdrant_client.local.distances import ( DistanceOrder, + QueryVector, + RecoQuery, + calculate_best_scores, calculate_distance, distance_to_order, ) @@ -93,17 +97,21 @@ def load(self) -> None: self.deleted = np.zeros(len(self.payload), dtype=bool) @classmethod - def _resolve_vector_name( + def _resolve_query_vector_name( cls, query_vector: Union[ types.NumpyArray, Sequence[float], Tuple[str, List[float]], types.NamedVector, + RecoQuery, + Tuple[str, RecoQuery], ], - ) -> Tuple[str, types.NumpyArray]: + ) -> Tuple[str, QueryVector]: if isinstance(query_vector, tuple): name, vector = query_vector + if isinstance(vector, RecoQuery): + return name, vector elif isinstance(query_vector, types.NamedVector): name = query_vector.name vector = query_vector.vector @@ -113,6 +121,10 @@ def _resolve_vector_name( elif isinstance(query_vector, list): name = DEFAULT_VECTOR_NAME vector = query_vector + elif isinstance(query_vector, RecoQuery): + name = DEFAULT_VECTOR_NAME + vector = query_vector + return name, vector else: raise ValueError(f"Unsupported vector type {type(query_vector)}") @@ -189,8 +201,9 @@ def search( query_vector: Union[ types.NumpyArray, Sequence[float], - Tuple[str, List[float]], types.NamedVector, + RecoQuery, + Tuple[str, Union[RecoQuery, types.NumpyArray, List[float]]], ], query_filter: Optional[types.Filter] = None, limit: int = 10, @@ -204,7 +217,7 @@ def search( payload_filter=query_filter, ids_inv=self.ids_inv, ) - name, vector = self._resolve_vector_name(query_vector) + name, query_vector = self._resolve_query_vector_name(query_vector) result: List[models.ScoredPoint] = [] @@ -213,7 +226,18 @@ def search( vectors = self.vectors[name] params = self.get_vector_params(name) - scores = calculate_distance(vector, vectors[: len(self.payload)], params.distance) + + if isinstance(query_vector, np.ndarray): + scores = calculate_distance( + query_vector, vectors[: len(self.payload)], params.distance + ) + elif isinstance(query_vector, RecoQuery): + scores = calculate_best_scores( + query_vector, vectors[: len(self.payload)], params.distance + ) + else: + raise(ValueError(f"Unsupported query vector type {type(query_vector)}")) + # in deleted: 1 - deleted, 0 - not deleted # in payload_mask: 1 - accepted, 0 - rejected # in mask: 1 - ok, 0 - rejected @@ -262,8 +286,9 @@ def search_groups( query_vector: Union[ types.NumpyArray, Sequence[float], - Tuple[str, List[float]], + Tuple[str, Union[List[float], RecoQuery, types.NumpyArray]], types.NamedVector, + RecoQuery, ], group_by: str, query_filter: Optional[models.Filter] = None, @@ -353,40 +378,86 @@ def retrieve( return result - def _recommend( + def _preprocess_recommend( self, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, + strategy: Optional[types.RecommendStrategy] = None, query_filter: Optional[types.Filter] = None, using: Optional[str] = None, lookup_from_collection: Optional["LocalCollection"] = None, lookup_from_vector_name: Optional[str] = None, - ) -> Tuple[str, List[float], Optional[types.Filter]]: - collection = self if lookup_from_collection is None else lookup_from_collection + ) -> Tuple[List[List[float]], List[List[float]], types.Filter]: + collection = lookup_from_collection if lookup_from_collection is not None else self search_in_vector_name = using if using is not None else DEFAULT_VECTOR_NAME vector_name = ( - search_in_vector_name if lookup_from_vector_name is None else lookup_from_vector_name + lookup_from_vector_name + if lookup_from_vector_name is not None + else search_in_vector_name ) - if len(positive) == 0: - raise ValueError("Positive list is empty") + positive = positive if positive is not None else [] + negative = negative if negative is not None else [] + + # Validate input depending on strategy + if strategy == types.RecommendStrategy.AVERAGE_VECTOR: + if len(positive) == 0: + raise ValueError("Positive list is empty") + elif strategy == types.RecommendStrategy.BEST_SCORE: + if len(positive) == 0 and len(negative) == 0: + raise ValueError("No positive or negative examples given") + # Turn every example into vectors positive_vectors = [] negative_vectors = [] + mentioned_ids: List[ExtendedPointId] = [] - for point_id in positive: - if point_id not in collection.ids: - raise ValueError(f"Point {point_id} is not found in the collection") + for example in positive: + if isinstance(example, get_args(types.PointId)): + if example not in collection.ids: + raise ValueError(f"Point {example} is not found in the collection") - idx = collection.ids[point_id] - positive_vectors.append(collection.vectors[vector_name][idx]) + idx = collection.ids[example] + positive_vectors.append(collection.vectors[vector_name][idx]) # type: ignore + mentioned_ids.append(example) + else: + positive_vectors.append(example) + + for example in negative: + if isinstance(example, get_args(types.PointId)): + if example not in collection.ids: + raise ValueError(f"Point {example} is not found in the collection") - for point_id in negative or []: - if point_id not in collection.ids: - raise ValueError(f"Point {point_id} is not found in the collection") + idx = collection.ids[example] + negative_vectors.append(collection.vectors[vector_name][idx]) # type: ignore + mentioned_ids.append(example) + else: + negative_vectors.append(example) - idx = collection.ids[point_id] - negative_vectors.append(collection.vectors[vector_name][idx]) + # Edit query filter + ignore_mentioned_ids = models.HasIdCondition(has_id=mentioned_ids) + + if query_filter is None: + query_filter = models.Filter(must_not=[ignore_mentioned_ids]) + else: + if query_filter.must_not is None: # type: ignore + query_filter.must_not = [ignore_mentioned_ids] + else: + query_filter.must_not.append(ignore_mentioned_ids) + + return positive_vectors, negative_vectors, query_filter # type: ignore + + def _recommend_average( + self, + positive_vectors: Optional[Sequence[List[float]]] = None, + negative_vectors: Optional[Sequence[List[float]]] = None, + ) -> types.NumpyArray: + positive_vectors = positive_vectors if positive_vectors is not None else [] + negative_vectors = negative_vectors if negative_vectors is not None else [] + + # Validate input + if len(positive_vectors) == 0: + raise ValueError("Positive list is empty") positive_vectors_np = np.stack(positive_vectors) negative_vectors_np = np.stack(negative_vectors) if len(negative_vectors) > 0 else None @@ -400,25 +471,12 @@ def _recommend( else: vector = mean_positive_vector - ignore_mentioned_ids = models.HasIdCondition( - has_id=list(positive) + (list(negative) if negative else []) - ) - - if query_filter is None: - query_filter = models.Filter(must_not=[ignore_mentioned_ids]) - else: - if query_filter.must_not is None: - query_filter.must_not = [ignore_mentioned_ids] - else: - query_filter.must_not.append(ignore_mentioned_ids) - - res_vector: List[float] = vector.tolist() # type: ignore - return search_in_vector_name, res_vector, query_filter + return vector def recommend( self, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, query_filter: Optional[types.Filter] = None, limit: int = 10, offset: int = 0, @@ -428,19 +486,38 @@ def recommend( using: Optional[str] = None, lookup_from_collection: Optional["LocalCollection"] = None, lookup_from_vector_name: Optional[str] = None, + strategy: Optional[types.RecommendStrategy] = None, ) -> List[models.ScoredPoint]: - search_in_vector_name, vector, query_filter = self._recommend( + strategy = strategy if strategy is not None else types.RecommendStrategy.AVERAGE_VECTOR + + positive_vectors, negative_vectors, edited_query_filter = self._preprocess_recommend( positive, negative, + strategy, query_filter, using, lookup_from_collection, lookup_from_vector_name, ) + if strategy == types.RecommendStrategy.AVERAGE_VECTOR: + query_vector = self._recommend_average( + positive_vectors, + negative_vectors, + ) + elif strategy == types.RecommendStrategy.BEST_SCORE: + query_vector = RecoQuery( + positive=positive_vectors, + negative=negative_vectors, + ) + else: + raise ValueError(f"strategy `{strategy}` is not a valid strategy, choose one from {types.RecommendStrategy}") + + search_in_vector_name = using if using is not None else DEFAULT_VECTOR_NAME + return self.search( - query_vector=(search_in_vector_name, vector), - query_filter=query_filter, + query_vector=(search_in_vector_name, query_vector), + query_filter=edited_query_filter, limit=limit, offset=offset, with_payload=with_payload, @@ -451,8 +528,8 @@ def recommend( def recommend_groups( self, group_by: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, query_filter: Optional[models.Filter] = None, limit: int = 10, group_size: int = 1, @@ -464,18 +541,36 @@ def recommend_groups( lookup_from_vector_name: Optional[str] = None, with_lookup: Optional[types.WithLookupInterface] = None, with_lookup_collection: Optional["LocalCollection"] = None, + strategy: Optional[types.RecommendStrategy] = None, ) -> types.GroupsResult: - search_in_vector_name, vector, query_filter = self._recommend( + strategy = strategy if strategy is not None else types.RecommendStrategy.AVERAGE_VECTOR + + positive_vectors, negative_vectors, edited_query_filter = self._preprocess_recommend( positive, negative, + strategy, query_filter, using, lookup_from_collection, lookup_from_vector_name, ) + + if strategy == types.RecommendStrategy.AVERAGE_VECTOR: + query_vector = self._recommend_average( + positive_vectors, + negative_vectors, + ) + elif strategy == types.RecommendStrategy.BEST_SCORE: + query_vector = RecoQuery( + positive=positive_vectors, + negative=negative_vectors, + ) + + search_in_vector_name = using if using is not None else DEFAULT_VECTOR_NAME + return self.search_groups( - query_vector=(search_in_vector_name, vector), - query_filter=query_filter, + query_vector=(search_in_vector_name, query_vector), + query_filter=edited_query_filter, group_by=group_by, group_size=group_size, limit=limit, diff --git a/qdrant_client/local/qdrant_local.py b/qdrant_client/local/qdrant_local.py index c4045cd8..e092945b 100644 --- a/qdrant_client/local/qdrant_local.py +++ b/qdrant_client/local/qdrant_local.py @@ -14,6 +14,7 @@ from qdrant_client.client_base import QdrantBase from qdrant_client.conversions import common_types as types from qdrant_client.http import models as rest_models +from qdrant_client.http.models.models import RecommendExample from qdrant_client.local.local_collection import LocalCollection META_INFO_FILENAME = "meta.json" @@ -230,8 +231,8 @@ def recommend_batch( def recommend( self, collection_name: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[RecommendExample]] = None, + negative: Optional[Sequence[RecommendExample]] = None, query_filter: Optional[types.Filter] = None, search_params: Optional[types.SearchParams] = None, limit: int = 10, @@ -241,6 +242,7 @@ def recommend( score_threshold: Optional[float] = None, using: Optional[str] = None, lookup_from: Optional[types.LookupLocation] = None, + strategy: Optional[types.RecommendStrategy] = None, **kwargs: Any, ) -> List[types.ScoredPoint]: collection = self._get_collection(collection_name) @@ -258,14 +260,15 @@ def recommend( if lookup_from else None, lookup_from_vector_name=lookup_from.vector if lookup_from else None, + strategy=strategy, ) def recommend_groups( self, collection_name: str, group_by: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[Union[types.PointId, List[float]]]] = None, + negative: Optional[Sequence[Union[types.PointId, List[float]]]] = None, query_filter: Optional[types.Filter] = None, search_params: Optional[types.SearchParams] = None, limit: int = 10, @@ -276,6 +279,7 @@ def recommend_groups( using: Optional[str] = None, lookup_from: Optional[types.LookupLocation] = None, with_lookup: Optional[types.WithLookupInterface] = None, + strategy: Optional[types.RecommendStrategy] = None, **kwargs: Any, ) -> types.GroupsResult: collection = self._get_collection(collection_name) @@ -303,6 +307,7 @@ def recommend_groups( lookup_from_vector_name=lookup_from.vector if lookup_from else None, with_lookup=with_lookup, with_lookup_collection=with_lookup_collection, + strategy=strategy, ) def scroll( diff --git a/qdrant_client/proto/collections.proto b/qdrant_client/proto/collections.proto index 687b6025..31fd0a00 100644 --- a/qdrant_client/proto/collections.proto +++ b/qdrant_client/proto/collections.proto @@ -278,12 +278,14 @@ message CollectionParams { optional VectorsConfig vectors_config = 5; // Configuration for vectors optional uint32 replication_factor = 6; // Number of replicas of each shard that network tries to maintain optional uint32 write_consistency_factor = 7; // How many replicas should apply the operation for us to consider it successful + optional uint32 read_fan_out_factor = 8; // Fan-out every read request to these many additional remote nodes (and return first available response) } message CollectionParamsDiff { optional uint32 replication_factor = 1; // Number of replicas of each shard that network tries to maintain optional uint32 write_consistency_factor = 2; // How many replicas should apply the operation for us to consider it successful optional bool on_disk_payload = 3; // If true - point's payload will not be stored in memory + optional uint32 read_fan_out_factor = 4; // Fan-out every read request to these many additional remote nodes (and return first available response) } message CollectionConfig { diff --git a/qdrant_client/proto/points.proto b/qdrant_client/proto/points.proto index 5f73ffdf..01eb794c 100644 --- a/qdrant_client/proto/points.proto +++ b/qdrant_client/proto/points.proto @@ -186,7 +186,7 @@ message QuantizationSearchParams { optional bool ignore = 1; /* - If true, use original vectors to re-score top-k results. Default is true. + If true, use original vectors to re-score top-k results. If ignored, qdrant decides automatically does rescore enabled or not. */ optional bool rescore = 2; @@ -281,6 +281,18 @@ message ScrollPoints { optional ReadConsistency read_consistency = 8; // Options for specifying read consistency guarantees } +// How to use positive and negative vectors to find the results, default is `AverageVector`: +enum RecommendStrategy { + // Average positive and negative vectors and create a single query with the formula + // `query = avg_pos + avg_pos - avg_neg`. Then performs normal search. + AverageVector = 0; + + // Uses custom search objective. Each candidate is compared against all + // examples, its score is then chosen from the `max(max_pos_score, max_neg_score)`. + // If the `max_neg_score` is chosen then it is squared and negated. + BestScore = 1; +} + message LookupLocation { string collection_name = 1; optional string vector_name = 2; // Which vector to use for search, if not specified - use default vector @@ -288,8 +300,8 @@ message LookupLocation { message RecommendPoints { string collection_name = 1; // name of the collection - repeated PointId positive = 2; // Look for vectors closest to those - repeated PointId negative = 3; // Try to avoid vectors like this + repeated PointId positive = 2; // Look for vectors closest to the vectors from these points + repeated PointId negative = 3; // Try to avoid vectors like the vector from these points Filter filter = 4; // Filter conditions - return only those points that satisfy the specified conditions uint64 limit = 5; // Max number of result reserved 6; // deprecated "with_vector" field @@ -301,6 +313,9 @@ message RecommendPoints { optional WithVectorsSelector with_vectors = 12; // Options for specifying which vectors to include into response optional LookupLocation lookup_from = 13; // Name of the collection to use for points lookup, if not specified - use current collection optional ReadConsistency read_consistency = 14; // Options for specifying read consistency guarantees + optional RecommendStrategy strategy = 16; // How to use the example vectors to find the results + repeated Vector positive_vectors = 17; // Look for vectors closest to those + repeated Vector negative_vectors = 18; // Try to avoid vectors like this } message RecommendBatchPoints { @@ -311,8 +326,8 @@ message RecommendBatchPoints { message RecommendPointGroups { string collection_name = 1; // Name of the collection - repeated PointId positive = 2; // Look for vectors closest to those - repeated PointId negative = 3; // Try to avoid vectors like this + repeated PointId positive = 2; // Look for vectors closest to the vectors from these points + repeated PointId negative = 3; // Try to avoid vectors like the vector from these points Filter filter = 4; // Filter conditions - return only those points that satisfy the specified conditions uint32 limit = 5; // Max number of groups in result WithPayloadSelector with_payload = 6; // Options for specifying which payload to include or not @@ -325,6 +340,9 @@ message RecommendPointGroups { uint32 group_size = 13; // Maximum amount of points to return per group optional ReadConsistency read_consistency = 14; // Options for specifying read consistency guarantees optional WithLookup with_lookup = 15; // Options for specifying how to use the group id to lookup points in another collection + optional RecommendStrategy strategy = 17; // How to use the example vectors to find the results + repeated Vector positive_vectors = 18; // Look for vectors closest to those + repeated Vector negative_vectors = 19; // Try to avoid vectors like this } message CountPoints { diff --git a/qdrant_client/proto/points_service.proto b/qdrant_client/proto/points_service.proto index 607e1169..59217773 100644 --- a/qdrant_client/proto/points_service.proto +++ b/qdrant_client/proto/points_service.proto @@ -64,7 +64,7 @@ service Points { */ rpc SearchGroups (SearchPointGroups) returns (SearchGroupsResponse) {} /* - Iterate over all or filtered points points + Iterate over all or filtered points */ rpc Scroll (ScrollPoints) returns (ScrollResponse) {} /* diff --git a/qdrant_client/qdrant_client.py b/qdrant_client/qdrant_client.py index 8441f5ed..3e64f71a 100644 --- a/qdrant_client/qdrant_client.py +++ b/qdrant_client/qdrant_client.py @@ -454,8 +454,8 @@ def recommend_batch( def recommend( self, collection_name: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, query_filter: Optional[types.Filter] = None, search_params: Optional[types.SearchParams] = None, limit: int = 10, @@ -465,6 +465,7 @@ def recommend( score_threshold: Optional[float] = None, using: Optional[str] = None, lookup_from: Optional[types.LookupLocation] = None, + strategy: Optional[types.RecommendStrategy] = None, consistency: Optional[types.ReadConsistency] = None, **kwargs: Any, ) -> List[types.ScoredPoint]: @@ -476,14 +477,16 @@ def recommend( Args: collection_name: Collection to search in positive: - List of stored point IDs, which should be used as reference for similarity search. - If there is only one ID provided - this request is equivalent to the regular search with vector of that + List of stored point IDs or vectors, which should be used as reference for similarity search. + If there is only one example - this request is equivalent to the regular search with vector of that point. - If there are more than one IDs, Qdrant will attempt to search for similar to all of them. - Recommendation for multiple vectors is experimental. Its behaviour may change in the future. + If there are more than one example, Qdrant will attempt to search for similar to all of them. + Recommendation for multiple vectors is experimental. + Its behaviour may change depending on selected strategy. negative: - List of stored point IDs, which should be dissimilar to the search result. - Negative examples is an experimental functionality. Its behaviour may change in the future. + List of stored point IDs or vectors, which should be dissimilar to the search result. + Negative examples is an experimental functionality. + Its behaviour may change depending on selected strategy. query_filter: - Exclude vectors which doesn't fit given conditions. - If `None` - search among all vectors @@ -523,6 +526,12 @@ def recommend( - 'majority' - query all replicas, but return values present in the majority of replicas - 'quorum' - query the majority of replicas, return values present in all of them - 'all' - query all replicas, and return values present in all replicas + strategy: + Strategy to use for recommendation. + Strategy defines how to combine multiple examples into a recommendation query. + Possible values: + - 'average_vector' - calculates average vector of all examples and uses it for search + - 'best_score' - finds the result which is closer to positive examples and further from negative Returns: List of recommended points with similarity scores. @@ -543,6 +552,7 @@ def recommend( using=using, lookup_from=lookup_from, consistency=consistency, + strategy=strategy, **kwargs, ) @@ -550,8 +560,8 @@ def recommend_groups( self, collection_name: str, group_by: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, query_filter: Optional[types.Filter] = None, search_params: Optional[types.SearchParams] = None, limit: int = 10, @@ -562,6 +572,7 @@ def recommend_groups( using: Optional[str] = None, lookup_from: Optional[types.LookupLocation] = None, with_lookup: Optional[types.WithLookupInterface] = None, + strategy: Optional[types.RecommendStrategy] = None, consistency: Optional[types.ReadConsistency] = None, **kwargs: Any, ) -> types.GroupsResult: @@ -575,14 +586,16 @@ def recommend_groups( Args: collection_name: Collection to search in positive: - List of stored point IDs, which should be used as reference for similarity search. - If there is only one ID provided - this request is equivalent to the regular search with vector of that + List of stored point IDs or vectors, which should be used as reference for similarity search. + If there is only one example - this request is equivalent to the regular search with vector of that point. - If there are more than one IDs, Qdrant will attempt to search for similar to all of them. - Recommendation for multiple vectors is experimental. Its behaviour may change in the future. + If there are more than one example, Qdrant will attempt to search for similar to all of them. + Recommendation for multiple vectors is experimental. + Its behaviour may change depending on selected strategy. negative: - List of stored point IDs, which should be dissimilar to the search result. - Negative examples is an experimental functionality. Its behaviour may change in the future. + List of stored point IDs or vectors, which should be dissimilar to the search result. + Negative examples is an experimental functionality. + Its behaviour may change depending on selected strategy. group_by: Name of the payload field to group by. Field must be of type "keyword" or "integer". Nested fields are specified using dot notation, e.g. "nested_field.subfield". @@ -627,6 +640,12 @@ def recommend_groups( - 'majority' - query all replicas, but return values present in the majority of replicas - 'quorum' - query the majority of replicas, return values present in all of them - 'all' - query all replicas, and return values present in all replicas + strategy: + Strategy to use for recommendation. + Strategy defines how to combine multiple examples into a recommendation query. + Possible values: + - 'average_vector' - calculates average vector of all examples and uses it for search + - 'best_score' - finds the result which is closer to positive examples and further from negative Returns: List of groups with not more than `group_size` hits in each group. @@ -651,6 +670,7 @@ def recommend_groups( lookup_from=lookup_from, consistency=consistency, with_lookup=with_lookup, + strategy=strategy, **kwargs, ) diff --git a/qdrant_client/qdrant_remote.py b/qdrant_client/qdrant_remote.py index a6d4827a..f586a341 100644 --- a/qdrant_client/qdrant_remote.py +++ b/qdrant_client/qdrant_remote.py @@ -633,8 +633,8 @@ def recommend_batch( def recommend( self, collection_name: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[types.RecommendExample]] = None, + negative: Optional[Sequence[types.RecommendExample]] = None, query_filter: Optional[types.Filter] = None, search_params: Optional[types.SearchParams] = None, limit: int = 10, @@ -644,26 +644,22 @@ def recommend( score_threshold: Optional[float] = None, using: Optional[str] = None, lookup_from: Optional[types.LookupLocation] = None, + strategy: Optional[types.RecommendStrategy] = None, consistency: Optional[types.ReadConsistency] = None, **kwargs: Any, ) -> List[types.ScoredPoint]: + if positive is None: + positive = [] + if negative is None: negative = [] if self._prefer_grpc: - positive = [ - RestToGrpc.convert_extended_point_id(point_id) - if isinstance(point_id, get_args_subscribed(models.ExtendedPointId)) - else point_id - for point_id in positive - ] + positive_ids = RestToGrpc.convert_recommend_examples_to_ids(positive) + positive_vectors = RestToGrpc.convert_recommend_examples_to_vectors(positive) - negative = [ - RestToGrpc.convert_extended_point_id(point_id) - if isinstance(point_id, get_args_subscribed(models.ExtendedPointId)) - else point_id - for point_id in negative - ] + negative_ids = RestToGrpc.convert_recommend_examples_to_ids(negative) + negative_vectors = RestToGrpc.convert_recommend_examples_to_vectors(negative) if isinstance(query_filter, models.Filter): query_filter = RestToGrpc.convert_filter(model=query_filter) @@ -683,11 +679,14 @@ def recommend( if isinstance(consistency, get_args_subscribed(models.ReadConsistency)): consistency = RestToGrpc.convert_read_consistency(consistency) + if isinstance(strategy, models.RecommendStrategy): + strategy = RestToGrpc.convert_recommend_strategy(strategy) + res: grpc.SearchResponse = self.grpc_points.Recommend( grpc.RecommendPoints( collection_name=collection_name, - positive=positive, - negative=negative, + positive=positive_ids, + negative=negative_ids, filter=query_filter, limit=limit, offset=offset, @@ -698,6 +697,9 @@ def recommend( using=using, lookup_from=lookup_from, read_consistency=consistency, + strategy=strategy, + positive_vectors=positive_vectors, + negative_vectors=negative_vectors, ), timeout=self._timeout, ) @@ -705,17 +707,17 @@ def recommend( return [GrpcToRest.convert_scored_point(hit) for hit in res.result] else: positive = [ - GrpcToRest.convert_point_id(point_id) - if isinstance(point_id, grpc.PointId) - else point_id - for point_id in positive + GrpcToRest.convert_point_id(example) + if isinstance(example, grpc.PointId) + else example + for example in positive ] negative = [ - GrpcToRest.convert_point_id(point_id) - if isinstance(point_id, grpc.PointId) - else point_id - for point_id in negative + GrpcToRest.convert_point_id(example) + if isinstance(example, grpc.PointId) + else example + for example in negative ] if isinstance(query_filter, grpc.Filter): @@ -745,6 +747,7 @@ def recommend( score_threshold=score_threshold, lookup_from=lookup_from, using=using, + strategy=strategy, ), ).result assert result is not None, "Recommend points API returned None" @@ -754,8 +757,8 @@ def recommend_groups( self, collection_name: str, group_by: str, - positive: Sequence[types.PointId], - negative: Optional[Sequence[types.PointId]] = None, + positive: Optional[Sequence[Union[types.PointId, List[float]]]] = None, + negative: Optional[Sequence[Union[types.PointId, List[float]]]] = None, query_filter: Optional[models.Filter] = None, search_params: Optional[models.SearchParams] = None, limit: int = 10, @@ -766,11 +769,12 @@ def recommend_groups( using: Optional[str] = None, lookup_from: Optional[models.LookupLocation] = None, with_lookup: Optional[types.WithLookupInterface] = None, - consistency: Optional[models.ReadConsistencyType] = None, + strategy: Optional[types.RecommendStrategy] = None, + consistency: Optional[types.ReadConsistency] = None, **kwargs: Any, ) -> types.GroupsResult: - if negative is None: - negative = [] + positive = positive if positive is not None else [] + negative = negative if negative is not None else [] if self._prefer_grpc: if isinstance(with_lookup, models.WithLookup): @@ -779,20 +783,12 @@ def recommend_groups( if isinstance(with_lookup, str): with_lookup = grpc.WithLookup(lookup_index=with_lookup) - positive = [ - RestToGrpc.convert_extended_point_id(point_id) - if isinstance(point_id, get_args_subscribed(models.ExtendedPointId)) - else point_id - for point_id in positive - ] - - negative = [ - RestToGrpc.convert_extended_point_id(point_id) - if isinstance(point_id, get_args_subscribed(models.ExtendedPointId)) - else point_id - for point_id in negative - ] + positive_ids = RestToGrpc.convert_recommend_examples_to_ids(positive) + positive_vectors = RestToGrpc.convert_recommend_examples_to_vectors(positive) + negative_ids = RestToGrpc.convert_recommend_examples_to_ids(negative) + negative_vectors = RestToGrpc.convert_recommend_examples_to_vectors(negative) + if isinstance(query_filter, models.Filter): query_filter = RestToGrpc.convert_filter(model=query_filter) @@ -811,11 +807,14 @@ def recommend_groups( if isinstance(consistency, get_args_subscribed(models.ReadConsistency)): consistency = RestToGrpc.convert_read_consistency(consistency) + if isinstance(strategy, models.RecommendStrategy): + strategy = RestToGrpc.convert_recommend_strategy(strategy) + res: grpc.GroupsResult = self.grpc_points.RecommendGroups( grpc.RecommendPointGroups( collection_name=collection_name, - positive=positive, - negative=negative, + positive=positive_ids, + negative=negative_ids, filter=query_filter, group_by=group_by, limit=limit, @@ -828,6 +827,9 @@ def recommend_groups( lookup_from=lookup_from, read_consistency=consistency, with_lookup=with_lookup, + strategy=strategy, + positive_vectors=positive_vectors, + negative_vectors=negative_vectors, ), timeout=self._timeout, ).result @@ -882,6 +884,7 @@ def recommend_groups( lookup_from=lookup_from, using=using, with_lookup=with_lookup, + strategy=strategy, ), ).result diff --git a/tests/congruence_tests/test_common.py b/tests/congruence_tests/test_common.py index 9700d2a9..501f4587 100644 --- a/tests/congruence_tests/test_common.py +++ b/tests/congruence_tests/test_common.py @@ -1,3 +1,4 @@ +import math from typing import Any, Callable, Dict, List, Optional, Union import numpy as np @@ -117,9 +118,11 @@ def compare_scored_record( assert ( point1.id == point2.id ), f"point1[{idx}].id = {point1.id}, point2[{idx}].id = {point2.id}" + # adjust precision depending on the magnitude of score + max_difference = 1e-4 * 10**(math.floor(math.log(abs(point2.score), 10))) assert ( - point1.score - point2.score < 1e-4 - ), f"point1[{idx}].score = {point1.score}, point2[{idx}].score = {point2.score}" + abs(point1.score - point2.score) < max_difference + ), f"point1[{idx}].score = {point1.score}, point2[{idx}].score = {point2.score}, max_difference = {max_difference}" assert ( point1.payload == point2.payload ), f"point1[{idx}].payload = {point1.payload}, point2[{idx}].payload = {point2.payload}" diff --git a/tests/congruence_tests/test_group_recommend.py b/tests/congruence_tests/test_group_recommend.py index c66d1c39..c688ee52 100644 --- a/tests/congruence_tests/test_group_recommend.py +++ b/tests/congruence_tests/test_group_recommend.py @@ -35,6 +35,20 @@ def simple_recommend_groups_image(self, client: QdrantBase) -> models.GroupsResu group_size=self.group_size, search_params=models.SearchParams(exact=True), ) + + def simple_recommend_groups_best_scores(self, client: QdrantBase) -> models.GroupsResult: + return client.recommend_groups( + collection_name=COLLECTION_NAME, + positive=[10], + negative=[], + with_payload=models.PayloadSelectorExclude(exclude=["city.geo", "rand_number"]), + limit=10, + using="image", + strategy=models.RecommendStrategy.BEST_SCORE, + group_by=self.group_by, + group_size=self.group_size, + search_params=models.SearchParams(exact=True), + ) def many_recommend_groups(self, client: QdrantBase) -> models.GroupsResult: return client.recommend_groups( @@ -118,6 +132,9 @@ def test_simple_recommend_groups() -> None: compare_client_results( local_client, remote_client, recommender.simple_recommend_groups_image ) + compare_client_results( + local_client, remote_client, recommender.simple_recommend_groups_best_scores + ) compare_client_results(local_client, remote_client, recommender.many_recommend_groups) compare_client_results( local_client, remote_client, recommender.simple_recommend_groups_negative diff --git a/tests/congruence_tests/test_recommendation.py b/tests/congruence_tests/test_recommendation.py index 0c91ea76..568d6b26 100644 --- a/tests/congruence_tests/test_recommendation.py +++ b/tests/congruence_tests/test_recommendation.py @@ -1,5 +1,7 @@ from typing import List +import numpy as np + from qdrant_client.client_base import QdrantBase from qdrant_client.http.models import models from tests.congruence_tests.test_common import ( @@ -9,13 +11,18 @@ init_client, init_local, init_remote, + image_vector_size, ) from tests.fixtures.filters import one_random_filter_please secondary_collection_name = "secondary_collection" - class TestSimpleRecommendation: + + def __init__(self): + self.query_image = np.random.random(image_vector_size).tolist() + + @classmethod def simple_recommend_image(cls, client: QdrantBase) -> List[models.ScoredPoint]: return client.recommend( @@ -75,6 +82,62 @@ def filter_recommend_text( limit=10, using="text", ) + + @classmethod + def best_score_recommend(cls, client: QdrantBase) -> List[models.ScoredPoint]: + return client.recommend( + collection_name=COLLECTION_NAME, + positive=[10, 20,], + negative=[], + with_payload=True, + limit=10, + using="image", + strategy=models.RecommendStrategy.BEST_SCORE, + ) + + @classmethod + def only_negatives_best_score_recommend(cls, client: QdrantBase) -> List[models.ScoredPoint]: + return client.recommend( + collection_name=COLLECTION_NAME, + positive=None, + negative=[10, 12], + with_payload=True, + limit=10, + using="image", + strategy=models.RecommendStrategy.BEST_SCORE, + ) + + @classmethod + def avg_vector_recommend(cls, client: QdrantBase) -> List[models.ScoredPoint]: + return client.recommend( + collection_name=COLLECTION_NAME, + positive=[10, 13], + negative=[], + with_payload=True, + limit=10, + using="image", + strategy=models.RecommendStrategy.AVERAGE_VECTOR, + ) + + def recommend_from_raw_vectors(self, client: QdrantBase) -> List[models.ScoredPoint]: + return client.recommend( + collection_name=COLLECTION_NAME, + positive=[self.query_image], + negative=[], + with_payload=True, + limit=10, + using="image", + ) + + def recommend_from_raw_vectors_and_ids(self, client: QdrantBase) -> List[models.ScoredPoint]: + return client.recommend( + collection_name=COLLECTION_NAME, + positive=[self.query_image, 10], + negative=[], + with_payload=True, + limit=10, + using="image", + ) def test_simple_recommend() -> None: @@ -96,8 +159,13 @@ def test_simple_recommend() -> None: compare_client_results(local_client, remote_client, searcher.many_recommend) compare_client_results(local_client, remote_client, searcher.simple_recommend_negative) compare_client_results(local_client, remote_client, searcher.recommend_from_another_collection) + compare_client_results(local_client, remote_client, searcher.best_score_recommend) + compare_client_results(local_client, remote_client, searcher.only_negatives_best_score_recommend) + compare_client_results(local_client, remote_client, searcher.avg_vector_recommend) + compare_client_results(local_client, remote_client, searcher.recommend_from_raw_vectors) + compare_client_results(local_client, remote_client, searcher.recommend_from_raw_vectors_and_ids) - for i in range(10): + for _ in range(10): query_filter = one_random_filter_please() try: compare_client_results( diff --git a/tests/conversions/fixtures.py b/tests/conversions/fixtures.py index 23864957..ce5f8938 100644 --- a/tests/conversions/fixtures.py +++ b/tests/conversions/fixtures.py @@ -176,6 +176,7 @@ vectors_config=multiple_vector_config, replication_factor=2, write_consistency_factor=1, + read_fan_out_factor=2, ) hnsw_config = grpc.HnswConfigDiff( @@ -562,6 +563,9 @@ with_vectors=grpc.WithVectorsSelector(enable=True), ) +recommend_strategy = grpc.RecommendStrategy.BestScore +recommend_strategy2 = grpc.RecommendStrategy.AverageVector + recommend_points = grpc.RecommendPoints( collection_name="collection-123", positive=[point_id_1, point_id_2], @@ -574,6 +578,14 @@ offset=10, using="abc", with_vectors=grpc.WithVectorsSelector(enable=True), + strategy=recommend_strategy, + positive_vectors=[ + grpc.Vector(data=[1.0, 2.0, -1.0, -0.2]), + grpc.Vector(data=[2.0, 2.0, -1.0, -0.2]), + ], + negative_vectors=[ + grpc.Vector(data=[3.0, 2.0, -1.0, -0.2]), + ], ) lookup_location_1 = grpc.LookupLocation( @@ -725,6 +737,12 @@ ), ) +delete_vectors_operation_2 = grpc.PointsUpdateOperation( + delete_vectors=grpc.PointsUpdateOperation.DeleteVectors( + points_selector=points_selector_filter, + vectors=grpc.VectorsSelector(names=["image", "text"]), + ), +) fixtures = { "CollectionParams": [collection_params, collection_params_2], @@ -786,6 +804,7 @@ "VectorsConfig": [single_vector_config, vector_config], "SearchPoints": [search_points, search_points_all_vectors], "RecommendPoints": [recommend_points], + "RecommendStrategy": [recommend_strategy, recommend_strategy2], "TextIndexParams": [ text_index_params_1, text_index_params_2, @@ -821,6 +840,7 @@ clear_payload_operation_2, update_vectors_operation, delete_vectors_operation, + delete_vectors_operation_2, ], } diff --git a/tests/conversions/test_validate_conversions.py b/tests/conversions/test_validate_conversions.py index 0a287582..478434e7 100644 --- a/tests/conversions/test_validate_conversions.py +++ b/tests/conversions/test_validate_conversions.py @@ -67,8 +67,10 @@ def test_conversion_completeness(): except Exception as e: logging.warning(f"Error with {fixture}") raise e - - if MessageToDict(grpc_fixture) != MessageToDict(fixture): + if isinstance(grpc_fixture, int): + # Is an enum + assert grpc_fixture == fixture, f"{model_class_name} conversion is broken" + elif MessageToDict(grpc_fixture) != MessageToDict(fixture): assert MessageToDict(grpc_fixture) == MessageToDict( fixture ), f"{model_class_name} conversion is broken" diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index 514a6ce3..b23bf8d4 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -2,5 +2,5 @@ set -ex -coverage run --include='qdrant_client/conversions/conversion.py' -m pytest tests/conversions/test_validate_conversions.py +coverage run --include='qdrant_client/conversions/conversion.py' -m pytest tests/conversions/test_validate_conversions.py -vv -s coverage report --fail-under=97 diff --git a/tests/test_qdrant_client.py b/tests/test_qdrant_client.py index bdcd1f86..db65c575 100644 --- a/tests/test_qdrant_client.py +++ b/tests/test_qdrant_client.py @@ -624,9 +624,17 @@ def test_qdrant_client_integration(prefer_grpc, numpy_upload, local_mode): for point in got_points: assert not point.payload + positive = [1, 2, query_vector.tolist()] + negative = [] + + if version is not None and version < "v1.6.0": + positive = [1, 2] + negative = [] + recommended_points = client.recommend( collection_name=COLLECTION_NAME, - positive=[1, 2], + positive=positive, + negative=negative, query_filter=Filter( must=[ # These conditions are required for recommend results FieldCondition( diff --git a/tests/type_stub.py b/tests/type_stub.py index 3a39a304..03993bf8 100644 --- a/tests/type_stub.py +++ b/tests/type_stub.py @@ -43,6 +43,7 @@ 1.0, "using", rest_models.LookupLocation(collection=""), + rest_models.RecommendStrategy.AVERAGE_VECTOR, 1, ) qdrant_client.recommend_batch(