Skip to content

Add if_not_exists parameter to create_collection#1149

Open
veeceey wants to merge 4 commits intoqdrant:masterfrom
veeceey:fix/issue-1022-create-collection-if-not-exists
Open

Add if_not_exists parameter to create_collection#1149
veeceey wants to merge 4 commits intoqdrant:masterfrom
veeceey:fix/issue-1022-create-collection-if-not-exists

Conversation

@veeceey
Copy link
Copy Markdown

@veeceey veeceey commented Feb 8, 2026

Summary

  • Adds if_not_exists boolean parameter to create_collection() method
  • When if_not_exists=True, only creates collection if it doesn't already exist
  • Implements for both QdrantClient and AsyncQdrantClient
  • Includes comprehensive tests for sync and async clients
  • Maintains backward compatibility (default if_not_exists=False)

Motivation

Currently users need to manually check if a collection exists before creating it:

if not client.collection_exists("test_collection"):
    client.create_collection(
        collection_name="test_collection"
    )

With this change, users can simplify this to:

client.create_collection(
    collection_name="test_collection",
    if_not_exists=True
)

Test Plan

  • Added tests for sync client with if_not_exists=True
  • Added tests for sync client without parameter (default behavior)
  • Added tests for async client with if_not_exists=True
  • Added tests for async client without parameter (default behavior)
  • All tests pass locally

Fixes

- Add if_not_exists boolean parameter to create_collection() method in both QdrantClient and AsyncQdrantClient
- When if_not_exists=True, only create collection if it doesn't already exist
- Prevents need for manual collection_exists() check before creation
- Add comprehensive tests for sync and async clients
- Maintain backward compatibility with default if_not_exists=False

Fixes qdrant#1022
@netlify
Copy link
Copy Markdown

netlify bot commented Feb 8, 2026

Deploy Preview for poetic-froyo-8baba7 ready!

Name Link
🔨 Latest commit 967c346
🔍 Latest deploy log https://app.netlify.com/projects/poetic-froyo-8baba7/deploys/69b241c5147922000838c4ee
😎 Deploy Preview https://deploy-preview-1149--poetic-froyo-8baba7.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 009f519e-458a-4327-97a0-6e02f1c9b83a

📥 Commits

Reviewing files that changed from the base of the PR and between 5f2a2eb and 967c346.

📒 Files selected for processing (1)
  • tests/test_create_collection_if_not_exists.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_create_collection_if_not_exists.py

📝 Walkthrough

Walkthrough

The PR adds an optional parameter if_not_exists: bool = False to create_collection in both QdrantClient and AsyncQdrantClient. When if_not_exists is True the method checks collection_exists() and returns True immediately if the collection already exists; otherwise it proceeds to create the collection. Docstrings were updated to describe the new parameter. Unit tests were added for both sync and async clients covering creation when the collection does or does not exist, idempotent calls with if_not_exists=True, and duplicate-creation error cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an if_not_exists parameter to the create_collection method.
Description check ✅ Passed The description is well-organized and directly related to the changeset, explaining the motivation, implementation details, test coverage, and backward compatibility.
Linked Issues check ✅ Passed The pull request fully implements the requirements from issue #1022: adds if_not_exists parameter to create_collection for both sync and async clients with appropriate behavior.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the if_not_exists feature: API updates to both clients, docstring updates, and comprehensive tests covering the new functionality.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@veeceey
Copy link
Copy Markdown
Author

veeceey commented Feb 8, 2026

All checks passing, ready for maintainer review

@veeceey
Copy link
Copy Markdown
Author

veeceey commented Feb 19, 2026

Thanks for the review, CodeRabbit! Good suggestion on adding a negative test case for if_not_exists=False on an existing collection. That would confirm the default behavior is preserved and document the expected failure mode. I'll add that test case and push an update.

Verify that creating an existing collection without if_not_exists=True
raises a ValueError, confirming the default behavior is preserved.
Covers both sync and async clients.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@veeceey
Copy link
Copy Markdown
Author

veeceey commented Feb 20, 2026

Added negative test cases as discussed — both sync and async tests now verify that creating an existing collection without if_not_exists=True correctly raises a ValueError. All 6 tests pass.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/test_create_collection_if_not_exists.py (2)

29-29: Cleanup calls are not guaranteed to run on assertion failure — prefer pytest.fixture or try/finally

Each test calls delete_collection (or await delete_collection) as the last statement. If any preceding assert fails, that call is skipped. While this has no practical inter-test impact here because every test spins up a fresh :memory: client, it establishes a fragile pattern that will break if tests are ever refactored to share a client or run against a real server.

A pytest.fixture with yield is the idiomatic fix:

♻️ Example refactor using `pytest.fixture`
import pytest
from qdrant_client import QdrantClient, models
from qdrant_client.async_qdrant_client import AsyncQdrantClient


`@pytest.fixture`
def sync_client():
    client = QdrantClient(":memory:")
    yield client
    client.close()


`@pytest.fixture`
async def async_client():
    client = AsyncQdrantClient(":memory:")
    yield client
    await client.close()


`@pytest.fixture`
def collection_name_sync(sync_client):
    name = "test_collection_if_not_exists"
    yield name
    if sync_client.collection_exists(name):
        sync_client.delete_collection(name)


def test_create_collection_if_not_exists_sync(sync_client, collection_name_sync):
    result = sync_client.create_collection(
        collection_name=collection_name_sync,
        vectors_config=models.VectorParams(size=10, distance=models.Distance.COSINE),
        if_not_exists=True,
    )
    assert result is True
    assert sync_client.collection_exists(collection_name_sync)
    # idempotent
    result = sync_client.create_collection(
        collection_name=collection_name_sync,
        vectors_config=models.VectorParams(size=10, distance=models.Distance.COSINE),
        if_not_exists=True,
    )
    assert result is True

Also applies to: 46-46, 73-73, 91-91, 110-110, 130-130

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_create_collection_if_not_exists.py` at line 29, Tests call
client.delete_collection (and await delete_collection) at the end of each test
which will be skipped if an assertion fails; refactor to use pytest fixtures
with yield to guarantee cleanup: create fixtures like sync_client (yields
QdrantClient(":memory:") and closes it after yield) and async_client (yields
AsyncQdrantClient and closes after yield), and create per-test collection_name
fixtures (e.g., collection_name_sync / collection_name_async) that yield the
collection name and, after yield, check client.collection_exists(name) and
delete_collection(name) (or await delete_collection) to ensure deletion even if
the test fails; update tests that reference create_collection_if_not_exists to
accept these fixtures and remove the trailing manual delete_collection calls.

50-73: Missing await client.close() for AsyncQdrantClient instances

AsyncQdrantClient holds underlying async transports/connections. Without closing, tests may produce ResourceWarning or unclosed-transport warnings and leave dangling handles in the event loop.

♻️ Minimal fix — wrap in try/finally until a fixture is adopted
 `@pytest.mark.asyncio`
 async def test_create_collection_if_not_exists_async():
     client = AsyncQdrantClient(":memory:")
     collection_name = "test_collection_if_not_exists_async"
-
-    result = await client.create_collection(...)
-    ...
-    await client.delete_collection(collection_name)
+    try:
+        result = await client.create_collection(...)
+        ...
+    finally:
+        await client.delete_collection(collection_name)
+        await client.close()

Also applies to: 77-91, 114-130

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_create_collection_if_not_exists.py` around lines 50 - 73, The
async test creates an AsyncQdrantClient but never closes it, which can leave
open transports; wrap the test body that uses AsyncQdrantClient (the client
variable in test_create_collection_if_not_exists_async and the other async tests
around lines mentioned) in a try/finally and call await client.close() in the
finally block (or ensure client.close() is awaited at the end of the test) so
underlying connections are properly closed; reference the AsyncQdrantClient
instance named client and methods
create_collection/collection_exists/delete_collection when locating where to add
the finally/await client.close() cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_create_collection_if_not_exists.py`:
- Line 29: Tests call client.delete_collection (and await delete_collection) at
the end of each test which will be skipped if an assertion fails; refactor to
use pytest fixtures with yield to guarantee cleanup: create fixtures like
sync_client (yields QdrantClient(":memory:") and closes it after yield) and
async_client (yields AsyncQdrantClient and closes after yield), and create
per-test collection_name fixtures (e.g., collection_name_sync /
collection_name_async) that yield the collection name and, after yield, check
client.collection_exists(name) and delete_collection(name) (or await
delete_collection) to ensure deletion even if the test fails; update tests that
reference create_collection_if_not_exists to accept these fixtures and remove
the trailing manual delete_collection calls.
- Around line 50-73: The async test creates an AsyncQdrantClient but never
closes it, which can leave open transports; wrap the test body that uses
AsyncQdrantClient (the client variable in
test_create_collection_if_not_exists_async and the other async tests around
lines mentioned) in a try/finally and call await client.close() in the finally
block (or ensure client.close() is awaited at the end of the test) so underlying
connections are properly closed; reference the AsyncQdrantClient instance named
client and methods create_collection/collection_exists/delete_collection when
locating where to add the finally/await client.close() cleanup.

- Use pytest fixtures with yield for sync and async clients
- Ensure AsyncQdrantClient.close() is always called
- Remove manual delete_collection calls that could be skipped on assertion failure
@veeceey
Copy link
Copy Markdown
Author

veeceey commented Mar 12, 2026

Refactored the tests per review feedback — all tests now use pytest fixtures with yield for reliable cleanup. The sync and async fixtures handle client creation and teardown, so cleanup runs even if assertions fail. Also ensured AsyncQdrantClient.close() is always called to prevent resource warnings.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_create_collection_if_not_exists.py`:
- Around line 42-50: The test test_create_collection_if_not_exists_false_sync
currently relies on the default for if_not_exists and doesn't validate the
explicit kwarg; update the call to sync_client.create_collection in that test to
pass if_not_exists=False explicitly (and do the same for the corresponding async
test around lines 79-87), ensuring you reference the same collection_name and
vectors_config parameters so the test exercises the explicit plumbing of the
if_not_exists argument rather than the default behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7498a7c6-375c-4969-a99d-9c0dacc88e0b

📥 Commits

Reviewing files that changed from the base of the PR and between 437d667 and 5f2a2eb.

📒 Files selected for processing (1)
  • tests/test_create_collection_if_not_exists.py

Comment thread tests/test_create_collection_if_not_exists.py
@veeceey
Copy link
Copy Markdown
Author

veeceey commented Apr 9, 2026

friendly bump - let me know if there's any feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create Collection if not exists

1 participant