|  | 
| 21 | 21 |     FilterCondition, | 
| 22 | 22 |     FilterOperator, | 
| 23 | 23 | ) | 
|  | 24 | +from qdrant_client import AsyncQdrantClient | 
|  | 25 | +from qdrant_client.http import models as qmodels | 
| 24 | 26 | 
 | 
| 25 | 27 | requires_qdrant_cluster = pytest.mark.skipif( | 
| 26 | 28 |     not os.getenv("QDRANT_CLUSTER_URL"), | 
| @@ -694,3 +696,125 @@ def test_create_payload_indexes_returns_early_when_no_payload_indexes( | 
| 694 | 696 |     vector_store: QdrantVectorStore, | 
| 695 | 697 | ): | 
| 696 | 698 |     vector_store._create_payload_indexes() | 
|  | 699 | + | 
|  | 700 | + | 
|  | 701 | +def test_sparse_vector_name_detection_switches_to_legacy() -> None: | 
|  | 702 | +    """If only legacy sparse name exists in collection, switch to it.""" | 
|  | 703 | +    mock_client = MagicMock(spec=QdrantClient) | 
|  | 704 | + | 
|  | 705 | +    class DummyParams: | 
|  | 706 | +        def __init__(self): | 
|  | 707 | +            self.vectors = {"text-dense": object()} | 
|  | 708 | +            self.sparse_vectors = {"text-sparse": object()} | 
|  | 709 | + | 
|  | 710 | +    class DummyConfig: | 
|  | 711 | +        def __init__(self): | 
|  | 712 | +            self.params = DummyParams() | 
|  | 713 | + | 
|  | 714 | +    class DummyCollection: | 
|  | 715 | +        def __init__(self): | 
|  | 716 | +            self.config = DummyConfig() | 
|  | 717 | + | 
|  | 718 | +    mock_client.collection_exists.return_value = True | 
|  | 719 | +    mock_client.get_collection.return_value = DummyCollection() | 
|  | 720 | + | 
|  | 721 | +    vs = QdrantVectorStore(collection_name="test_collection", client=mock_client) | 
|  | 722 | + | 
|  | 723 | +    assert vs.sparse_vector_name == "text-sparse" | 
|  | 724 | + | 
|  | 725 | + | 
|  | 726 | +def test_sparse_vector_name_detection_keeps_new() -> None: | 
|  | 727 | +    """If only new sparse name exists in collection, keep the default new name.""" | 
|  | 728 | +    mock_client = MagicMock(spec=QdrantClient) | 
|  | 729 | + | 
|  | 730 | +    class DummyParams: | 
|  | 731 | +        def __init__(self): | 
|  | 732 | +            self.vectors = {"text-dense": object()} | 
|  | 733 | +            self.sparse_vectors = {"text-sparse-new": object()} | 
|  | 734 | + | 
|  | 735 | +    class DummyConfig: | 
|  | 736 | +        def __init__(self): | 
|  | 737 | +            self.params = DummyParams() | 
|  | 738 | + | 
|  | 739 | +    class DummyCollection: | 
|  | 740 | +        def __init__(self): | 
|  | 741 | +            self.config = DummyConfig() | 
|  | 742 | + | 
|  | 743 | +    mock_client.collection_exists.return_value = True | 
|  | 744 | +    mock_client.get_collection.return_value = DummyCollection() | 
|  | 745 | + | 
|  | 746 | +    vs = QdrantVectorStore(collection_name="test_collection", client=mock_client) | 
|  | 747 | + | 
|  | 748 | +    assert vs.sparse_vector_name == "text-sparse-new" | 
|  | 749 | + | 
|  | 750 | + | 
|  | 751 | +def test_sparse_vector_name_respects_user_specified() -> None: | 
|  | 752 | +    """If a user specifies a sparse vector name present in the collection, don't override it.""" | 
|  | 753 | +    mock_client = MagicMock(spec=QdrantClient) | 
|  | 754 | + | 
|  | 755 | +    class DummyParams: | 
|  | 756 | +        def __init__(self): | 
|  | 757 | +            self.vectors = {"text-dense": object()} | 
|  | 758 | +            self.sparse_vectors = { | 
|  | 759 | +                "custom-sparse": object(), | 
|  | 760 | +                "text-sparse-new": object(), | 
|  | 761 | +            } | 
|  | 762 | + | 
|  | 763 | +    class DummyConfig: | 
|  | 764 | +        def __init__(self): | 
|  | 765 | +            self.params = DummyParams() | 
|  | 766 | + | 
|  | 767 | +    class DummyCollection: | 
|  | 768 | +        def __init__(self): | 
|  | 769 | +            self.config = DummyConfig() | 
|  | 770 | + | 
|  | 771 | +    mock_client.collection_exists.return_value = True | 
|  | 772 | +    mock_client.get_collection.return_value = DummyCollection() | 
|  | 773 | + | 
|  | 774 | +    vs = QdrantVectorStore( | 
|  | 775 | +        collection_name="test_collection", | 
|  | 776 | +        client=mock_client, | 
|  | 777 | +        sparse_vector_name="custom-sparse", | 
|  | 778 | +    ) | 
|  | 779 | + | 
|  | 780 | +    assert vs.sparse_vector_name == "custom-sparse" | 
|  | 781 | + | 
|  | 782 | + | 
|  | 783 | +@pytest.mark.asyncio | 
|  | 784 | +async def test_async_query_initializes_with_async_client_only() -> None: | 
|  | 785 | +    """ | 
|  | 786 | +    When only an async client is provided and the collection already exists, | 
|  | 787 | +    aquery should lazily detect vector format and successfully return results. | 
|  | 788 | +    """ | 
|  | 789 | +    collection_name = "async_init_test" | 
|  | 790 | +    aclient = AsyncQdrantClient(":memory:") | 
|  | 791 | + | 
|  | 792 | +    # Create collection with named dense vector | 
|  | 793 | +    await aclient.create_collection( | 
|  | 794 | +        collection_name=collection_name, | 
|  | 795 | +        vectors_config={ | 
|  | 796 | +            "text-dense": qmodels.VectorParams(size=2, distance=qmodels.Distance.COSINE) | 
|  | 797 | +        }, | 
|  | 798 | +    ) | 
|  | 799 | + | 
|  | 800 | +    # Insert a single point | 
|  | 801 | +    await aclient.upsert( | 
|  | 802 | +        collection_name=collection_name, | 
|  | 803 | +        points=[ | 
|  | 804 | +            qmodels.PointStruct( | 
|  | 805 | +                id="11111111-1111-1111-1111-111111111111", | 
|  | 806 | +                vector={"text-dense": [1.0, 0.0]}, | 
|  | 807 | +                payload={"text": "hello"}, | 
|  | 808 | +            ) | 
|  | 809 | +        ], | 
|  | 810 | +    ) | 
|  | 811 | + | 
|  | 812 | +    # Initialize store with async client only | 
|  | 813 | +    store = QdrantVectorStore(collection_name=collection_name, aclient=aclient) | 
|  | 814 | + | 
|  | 815 | +    query = VectorStoreQuery(query_embedding=[1.0, 0.0], similarity_top_k=1) | 
|  | 816 | +    result = await store.aquery(query) | 
|  | 817 | + | 
|  | 818 | +    assert result is not None | 
|  | 819 | +    assert len(result.nodes) == 1 | 
|  | 820 | +    assert getattr(result.nodes[0], "text", None) == "hello" | 
0 commit comments