import random from time import sleep import pytest from .helpers.helpers import request_with_validation from .helpers.collection_setup import drop_collection @pytest.fixture(autouse=True, scope="module") def setup(on_disk_vectors, on_disk_payload, collection_name): multivec_collection_setup(collection_name=collection_name, on_disk_vectors=on_disk_vectors, on_disk_payload=on_disk_payload) yield drop_collection(collection_name=collection_name) def multivec_collection_setup(collection_name='test_collection', on_disk_vectors=False, on_disk_payload=False): response = request_with_validation( api='/collections/{collection_name}', method="DELETE", path_params={'collection_name': collection_name}, ) assert response.ok response = request_with_validation( api='/collections/{collection_name}', method="PUT", path_params={'collection_name': collection_name}, body={ "vectors": { "image": { "size": 4, "distance": "Dot", "hnsw_config": { "m": 20, }, "on_disk": on_disk_vectors, }, "audio": { "size": 4, "distance": "Dot", "hnsw_config": { "ef_construct": 100 }, "quantization_config": { "scalar": { "type": "int8", "quantile": 0.6 } }, "on_disk": on_disk_vectors, }, "text": { "size": 8, "distance": "Cosine", "quantization_config": { "scalar": { "type": "int8", "always_ram": True } }, "on_disk": on_disk_vectors, }, }, "hnsw_config": { "m": 10, "ef_construct": 80 }, "quantization": { "scalar": { "type": "int8", "quantile": 0.5 } }, "on_disk_payload": on_disk_payload } ) assert response.ok response = request_with_validation( api='/collections/{collection_name}/points', method="PUT", path_params={'collection_name': collection_name}, query_params={'wait': 'true'}, body={ "points": [ { "id": 1, "vector": { "image": [0.05, 0.61, 0.76, 0.74], "audio": [0.05, 0.61, 0.76, 0.74], "text": [0.05, 0.61, 0.76, 0.74, 0.05, 0.61, 0.76, 0.74], }, "payload": {"city": "Berlin"} }, { "id": 2, "vector": { "image": [0.19, 0.81, 0.75, 0.11], "audio": [0.19, 0.81, 0.75, 0.11], "text": [0.19, 0.81, 0.75, 0.11, 0.19, 0.81, 0.75, 0.11], }, "payload": {"city": ["Berlin", "London"]} } ] } ) assert response.ok def test_retrieve_vector_specific_hnsw(on_disk_vectors, collection_name): response = request_with_validation( api='/collections/{collection_name}', method="GET", path_params={'collection_name': collection_name}, ) assert response.ok config = response.json()['result']['config'] vectors = config['params']['vectors'] assert vectors['image']['hnsw_config']['m'] == 20 assert 'ef_construct' not in vectors['image']['hnsw_config'] assert vectors['image']['on_disk'] == on_disk_vectors assert 'm' not in vectors['audio']['hnsw_config'] assert vectors['audio']['hnsw_config']['ef_construct'] == 100 assert vectors['audio']['on_disk'] == on_disk_vectors assert 'hnsw_config' not in vectors['text'] assert vectors['text']['on_disk'] == on_disk_vectors assert config['hnsw_config']['m'] == 10 assert config['hnsw_config']['ef_construct'] == 80 def test_retrieve_vector_specific_quantization(on_disk_vectors, collection_name): response = request_with_validation( api='/collections/{collection_name}', method="GET", path_params={'collection_name': collection_name}, ) assert response.ok config = response.json()['result']['config'] vectors = config['params']['vectors'] assert 'quantization_config' not in vectors['image'] assert vectors['image']['on_disk'] == on_disk_vectors assert vectors['audio']['quantization_config']['scalar']['type'] == "int8" assert vectors['audio']['quantization_config']['scalar']['quantile'] == 0.6 assert 'always_ram' not in vectors['audio']['quantization_config']['scalar'] assert vectors['audio']['on_disk'] == on_disk_vectors assert vectors['text']['quantization_config']['scalar']['type'] == "int8" assert 'quantile' not in vectors['text']['quantization_config']['scalar'] assert vectors['text']['quantization_config']['scalar']['always_ram'] assert vectors['text']['on_disk'] == on_disk_vectors assert config['quantization_config']['scalar']['type'] == "int8" assert config['quantization_config']['scalar']['quantile'] == 0.5 @pytest.mark.timeout(20) def test_disable_indexing(on_disk_vectors): indexed_name = 'test_collection_indexed' unindexed_name = 'test_collection_unindexed' drop_collection(collection_name=indexed_name) drop_collection(collection_name=unindexed_name) def create_collection(collection_name, indexing_threshold, on_disk_vectors): response = request_with_validation( api='/collections/{collection_name}', method="PUT", path_params={'collection_name': collection_name}, body={ "vectors": { "size": 256, "distance": "Dot", "on_disk": on_disk_vectors, }, "optimizers_config": { "indexing_threshold": indexing_threshold } } ) assert response.ok amount_of_vectors = 100 # Collection with indexing enabled create_collection(indexed_name, 10, on_disk_vectors) insert_vectors(indexed_name, amount_of_vectors) # Collection with indexing disabled create_collection(unindexed_name, 0, on_disk_vectors) insert_vectors(unindexed_name, amount_of_vectors) while True: try: # Get info indexed response = request_with_validation( method='GET', api='/collections/{collection_name}', path_params={'collection_name': indexed_name}, ) assert response.ok assert response.json()['result']['points_count'] == amount_of_vectors assert response.json()['result']['indexed_vectors_count'] > 0 # Get info unindexed response = request_with_validation( method='GET', api='/collections/{collection_name}', path_params={'collection_name': unindexed_name}, ) assert response.ok assert response.json()['result']['points_count'] == amount_of_vectors assert response.json()['result']['indexed_vectors_count'] == 0 break except AssertionError: sleep(0.1) continue # Cleanup drop_collection(collection_name=indexed_name) drop_collection(collection_name=unindexed_name) @pytest.mark.parametrize( "config_name,vector_config,expected_warnings", [ ("no_inline_storage", {}, 0), ( "valid_config", { "hnsw_config": {"inline_storage": True}, "quantization_config": {"scalar": {"type": "int8"}}, }, 0, ), ( "inline_storage_no_quant", {"hnsw_config": {"inline_storage": True}}, 1, ), ( "inline_storage_multivec", { "hnsw_config": {"inline_storage": True}, "multivector_config": {"comparator": "max_sim"}, }, 1, ), ], ) def test_configuration_warnings(config_name, vector_config, expected_warnings): test_collection = f"test_{config_name}" drop_collection(collection_name=test_collection) # Create collection with specified configuration base_vector_config = {"size": 4, "distance": "Dot"} base_vector_config.update(vector_config) response = request_with_validation( api="/collections/{collection_name}", method="PUT", path_params={"collection_name": test_collection}, body={"vectors": {"test_vector": base_vector_config}}, ) assert response.ok response = request_with_validation( api="/collections/{collection_name}", method="GET", path_params={"collection_name": test_collection}, ) assert response.ok if expected_warnings > 0: warnings = response.json()["result"]["warnings"] assert len(warnings) == expected_warnings else: assert "warnings" not in response.json()["result"] drop_collection(collection_name=test_collection) def insert_vectors(collection_name='test_collection', count=2000, size=256): ids = [x for x in range(count)] vectors = [[random.random() for _ in range(size)] for _ in range(count)] batch_size = 1000 start = 0 end = 0 while end < count: end = min(end + batch_size, count) response = request_with_validation( api='/collections/{collection_name}/points', method='PUT', path_params={'collection_name': collection_name}, query_params={'wait': 'true'}, body={ "batch": { "ids": ids[start:end], "vectors": vectors[start:end], } } ) assert response.ok start += batch_size