mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-02 16:10:58 -05:00
* submit an issue for unindexed field solve unindexed field issue when an index is created for the field also log a warning when issue is submitted fmt fix rebase problems get collection name without panicking better solving of issue remove outdated TODO add tests, fix GET /issues review fixes use `/` instead of `.` as separator fmt prepare extractor for time-based submission smol rename add check in search hook unindexed field creation in search and search_batch of rest update after rebase on `dev` remove submit from `struct_payload_index` solve issues inside of handlers, not in match router post-process at Collection level don't clone filters remove from openapi spec - Add hidden env variable to adjust slow search threshold - Remove Solution::None - Fix UnindexedField issue extractor - Add endpoint to openapi, but without response body spec - Move integration test to consensus_tests to set lower threshold * event-based integration * use typed Code * remove payload_index_schema hack, get through collection info * rename `notify` to `publish` * update after rebase * remove breakpoint * update after rebase * attach current payload schema to SlowQueryEvent * collect filters refs lazily * review fixes * review fixes * use regular config for setting the threshold, use 1.2 secs as default * fix checking against current schema for all conditions
145 lines
3.8 KiB
Python
145 lines
3.8 KiB
Python
import pytest
|
|
import requests
|
|
|
|
from .fixtures import create_collection, drop_collection, upsert_random_points
|
|
from .utils import kill_all_processes, start_cluster
|
|
|
|
COLL_NAME = "test_collection"
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def setup(tmp_path_factory: pytest.TempPathFactory):
|
|
extra_env = {
|
|
"QDRANT__SERVICE__SLOW_QUERY_SECS": "0.001", # "Always" try to trigger slow search issue
|
|
}
|
|
|
|
tmp_path = tmp_path_factory.mktemp("qdrant")
|
|
|
|
peer_api_uris, _peer_dirs, _bootstrap_uri = start_cluster(
|
|
tmp_path=tmp_path, num_peers=1, extra_env=extra_env
|
|
)
|
|
|
|
uri = peer_api_uris[0]
|
|
|
|
yield uri
|
|
|
|
kill_all_processes()
|
|
|
|
|
|
@pytest.fixture
|
|
def setup_with_big_collection(setup):
|
|
uri = setup
|
|
|
|
create_collection(uri, COLL_NAME)
|
|
|
|
upsert_random_points(uri, 10000, collection_name=COLL_NAME, with_sparse_vector=False)
|
|
|
|
yield uri
|
|
|
|
drop_collection(uri, COLL_NAME)
|
|
|
|
|
|
def get_issues(uri):
|
|
response = requests.get(
|
|
f"{uri}/issues",
|
|
)
|
|
assert response.ok
|
|
return response.json()["result"]["issues"]
|
|
|
|
|
|
def search_with_city_filter(uri):
|
|
response = requests.post(
|
|
f"{uri}/collections/{COLL_NAME}/points/search",
|
|
json={
|
|
"vector": [0.2, 0.1, 0.9, 0.7],
|
|
"limit": 1000,
|
|
"filter": {"must": [{"key": "city", "match": {"any": ["London", "Moscow"]}}]},
|
|
},
|
|
)
|
|
assert response.ok, response.json()
|
|
|
|
|
|
def test_unindexed_field_is_gone_when_deleting_collection(setup_with_big_collection):
|
|
uri = setup_with_big_collection
|
|
|
|
expected_issue_code = "UNINDEXED_FIELD/test_collection/city"
|
|
issues = get_issues(uri)
|
|
assert expected_issue_code not in [issue["code"] for issue in issues]
|
|
|
|
search_with_city_filter(uri)
|
|
|
|
issues = get_issues(uri)
|
|
|
|
# check the issue is now active
|
|
assert expected_issue_code in [issue["id"] for issue in issues]
|
|
|
|
# delete collection
|
|
drop_collection(uri, COLL_NAME)
|
|
|
|
# check the issue is not active anymore
|
|
issues = get_issues(uri)
|
|
assert expected_issue_code not in [issue["id"] for issue in issues]
|
|
|
|
|
|
def test_unindexed_field_is_gone_when_indexing(setup_with_big_collection):
|
|
uri = setup_with_big_collection
|
|
|
|
expected_issue_code = "UNINDEXED_FIELD/test_collection/city"
|
|
|
|
issues = get_issues(uri)
|
|
assert expected_issue_code not in [issue["id"] for issue in issues]
|
|
|
|
search_with_city_filter(uri)
|
|
|
|
issues = get_issues(uri)
|
|
|
|
# check the issue is now active
|
|
issue_present = False
|
|
for issue in issues:
|
|
if issue["id"] == expected_issue_code:
|
|
issue_present = True
|
|
solution = issue["solution"]["immediate"]["action"]
|
|
timestamp = issue["timestamp"]
|
|
break
|
|
assert issue_present
|
|
|
|
# search again
|
|
search_with_city_filter(uri)
|
|
|
|
issues = get_issues(uri)
|
|
|
|
# check the timestamp has not changed
|
|
issue_present = False
|
|
for issue in issues:
|
|
if issue["id"] == expected_issue_code:
|
|
issue_present = True
|
|
assert timestamp == issue["timestamp"]
|
|
break
|
|
assert issue_present
|
|
|
|
assert solution == {
|
|
"method": "PUT",
|
|
"uri": f"/collections/{COLL_NAME}/index",
|
|
"body": {"field_name": "city", "field_schema": "keyword"},
|
|
"headers": {"content-type": "application/json"},
|
|
}
|
|
|
|
# use provided solution to create index
|
|
response = requests.request(
|
|
method=solution["method"],
|
|
url=uri + solution["uri"],
|
|
json=solution["body"],
|
|
)
|
|
assert response.ok
|
|
|
|
# check the issue is not active anymore
|
|
issues = get_issues(uri)
|
|
assert expected_issue_code not in [issue["id"] for issue in issues]
|
|
|
|
# search again
|
|
search_with_city_filter(uri)
|
|
|
|
# check the issue is not triggered again
|
|
issues = get_issues(uri)
|
|
assert expected_issue_code not in [issue["id"] for issue in issues]
|