Files
qdrant/tests/consensus_tests/test_issues_api.py
Luis Cossío a4308f5308 Issues API: Unindexed fields (#4139)
* 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
2024-05-09 09:39:01 -04:00

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]