Skip to content

Commit

Permalink
Add the multi-tenancy for the indexes handling (#64)
Browse files Browse the repository at this point in the history
* *Add the multi-tenancy for the handling indexes
* Add the test cases.
* Call the delete method.
* *Drop the index before creating.
* * Pass the collection to drop index.
* * prepare the database for test drop indices.
* * Create new model movie and removed the index tat defined in meta.
* * Update the prepare database.
  • Loading branch information
harshalizode authored Dec 20, 2024
1 parent 27a4af5 commit 25707e1
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 13 deletions.
45 changes: 34 additions & 11 deletions mongoz/core/db/documents/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ async def update(
return self

@classmethod
async def create_many(cls: Type["Document"], models: List["Document"]) -> List["Document"]:
async def create_many(cls: Type["Document"], models: List["Document"],
collection: Union[Collection, AsyncIOMotorCollection, None] = None) -> List["Document"]:
"""
Insert many documents
"""
Expand All @@ -91,8 +92,10 @@ async def create_many(cls: Type["Document"], models: List["Document"]) -> List["
raise TypeError(f"All models must be of type {cls.__name__}")

data = (model.model_dump(exclude={"id"}) for model in models)
if isinstance(cls.meta.from_collection, AsyncIOMotorCollection):
results = await cls.meta.from_collection.insert_many(data)
if isinstance(collection, Collection):
results = await collection._collection.insert_many(data)
elif isinstance(collection, AsyncIOMotorCollection):
results = await collection.insert_many(data)
else:
results = await cls.meta.collection._collection.insert_many(data) # type: ignore
for model, inserted_id in zip(models, results.inserted_ids, strict=True):
Expand All @@ -109,20 +112,25 @@ def get_collection(
return collection if collection is not None else cls.meta.collection._collection # type: ignore

@classmethod
async def create_index(cls, name: str) -> str:
async def create_index(cls, name: str, collection: Union[Collection, AsyncIOMotorCollection, None] = None) -> str:
"""
Creates an index from the list of indexes of the Meta object.
"""
is_operation_allowed(cls)

for index in cls.meta.indexes:
if index.name == name:
await cls.meta.collection._collection.create_indexes([index]) # type: ignore
if isinstance(collection, Collection):
await collection._collection.create_indexes([index])
elif isinstance(collection, AsyncIOMotorCollection):
await collection.create_indexes([index])
else:
await cls.meta.collection._collection.create_indexes([index]) # type: ignore
return index.name
raise InvalidKeyError(f"Unable to find index: {name}")

@classmethod
async def create_indexes(cls) -> List[str]:
async def create_indexes(cls, collection: Union[Collection, AsyncIOMotorCollection, None] = None) -> List[str]:
"""
Create indexes defined for the collection or drop for existing ones.
Expand All @@ -135,7 +143,12 @@ async def create_indexes(cls) -> List[str]:
"""
is_operation_allowed(cls)
return await cls.meta.collection._collection.create_indexes(cls.meta.indexes) # type: ignore
if isinstance(collection, Collection):
return await collection._collection.create_indexes(cls.meta.indexes) # noqa
elif isinstance(collection, AsyncIOMotorCollection):
return await collection.create_indexes(cls.meta.indexes)
else:
return await cls.meta.collection._collection.create_indexes(cls.meta.indexes) # type: ignore

@classmethod
async def create_indexes_for_multiple_databases(
Expand Down Expand Up @@ -217,7 +230,7 @@ async def drop_indexes_for_multiple_databases(
await cls.check_indexes(force_drop=True, collection=collection)

@classmethod
async def list_indexes(cls) -> List[Dict[str, Any]]:
async def list_indexes(cls, collection: Union[Collection, AsyncIOMotorCollection, None] = None) -> List[Dict[str, Any]]:
"""
List all indexes in the collection.
Expand All @@ -232,8 +245,14 @@ async def list_indexes(cls) -> List[Dict[str, Any]]:
is_operation_allowed(cls)

collection_indexes = []
if isinstance(collection, Collection):
collection = collection._collection
elif isinstance(collection, AsyncIOMotorCollection):
pass
else:
collection = cls.meta.collection._collection # type: ignore

async for index in cls.meta.collection._collection.list_indexes(): # type: ignore
async for index in collection.list_indexes():
collection_indexes.append(index)
return collection_indexes

Expand Down Expand Up @@ -335,8 +354,12 @@ async def drop_indexes(

collection = cls.get_collection(collection)
if force:
await collection.drop_indexes()
return None
if isinstance(collection, Collection):
return await collection._collection.drop_indexes() # type: ignore
elif isinstance(collection, AsyncIOMotorCollection):
return await collection.drop_indexes() # type: ignore
else:
return await cls.meta.collection._collection.drop_indexes() # type: ignore
index_names = [await cls.drop_index(index.name) for index in cls.meta.indexes]
return index_names

Expand Down
2 changes: 1 addition & 1 deletion mongoz/core/db/querysets/core/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ async def create_many(self, models: List["Document"]) -> List["Document"]:
Creates many documents (bulk create).
"""
manager: "Manager" = self.clone()
return await manager.model_class.create_many(models=models) # type: ignore
return await manager.model_class.create_many(models=models, collection=manager._collection) # type: ignore

async def bulk_create(self, models: List["Document"]) -> List["Document"]:
"""
Expand Down
34 changes: 33 additions & 1 deletion tests/indexes/test_indexes_drop_indexes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional
from typing import AsyncGenerator, Optional

import pytest

Expand Down Expand Up @@ -26,6 +26,28 @@ class Meta:
autogenerate_index = True


class Movie(Document):
name: str = mongoz.String()
email: str = mongoz.Email(index=True, unique=True)
year: int = mongoz.Integer()
uuid: Optional[ObjectId] = mongoz.ObjectId(null=True)

class Meta:
registry = client
database = "test_db"
autogenerate_index = True


@pytest.fixture(scope="function", autouse=True)
async def prepare_database() -> AsyncGenerator:
collection = Movie.objects.using("another_test_db")._collection
await Movie.drop_indexes(force=True, collection=collection)
await Movie.objects.using("another_test_db").delete()
yield
await Movie.drop_indexes(force=True)
await Movie.objects.using("another_test_db").delete()


async def test_drops_indexes() -> None:
await AnotherMovie.create_indexes()
await AnotherMovie.objects.create(name="Mongoz", email="mongoz@mongoz.com", year=2023)
Expand Down Expand Up @@ -62,3 +84,13 @@ async def test_drops_indexes() -> None:
total_indexes = await AnotherMovie.list_indexes()

assert len(total_indexes) == 1


async def test_drops_indexes_different_db() -> None:
collection = Movie.objects.using("another_test_db")._collection
await Movie.create_indexes(collection=collection)
await Movie.objects.using("another_test_db").create(
name="Mongoz", email="mongoz@mongoz.com", year=2023)
total_indexes = await Movie.list_indexes(collection=collection)

assert len(total_indexes) == 2
41 changes: 41 additions & 0 deletions tests/models/manager/test_create_many.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,20 @@ class Meta:

@pytest.fixture(scope="function", autouse=True)
async def prepare_database() -> AsyncGenerator:
collection = Movie.objects.using("another_test_db")._collection
await Movie.drop_indexes(force=True)
await Movie.objects.delete()
await Movie.create_indexes()
await Movie.drop_indexes(force=True, collection=collection)
await Movie.objects.using("another_test_db").delete()
await Movie.create_indexes(collection=collection)
yield
await Movie.drop_indexes(force=True)
await Movie.objects.delete()
await Movie.create_indexes()
await Movie.drop_indexes(force=True, collection=collection)
await Movie.objects.using("another_test_db").delete()
await Movie.create_indexes(collection=collection)


@pytest.mark.skipif(sys.version_info < (3, 10), reason="zip() implementation refactored in 3.10+")
Expand Down Expand Up @@ -69,6 +76,40 @@ class Meta:
await Movie.objects.create_many([book, movie]) # type: ignore


@pytest.mark.skipif(sys.version_info < (3, 10), reason="zip() implementation refactored in 3.10+")
async def test_model_create_many_different_db() -> None:
db = "another_test_db"
movies = []
movie_names = ("The Dark Knight", "The Dark Knight Rises", "The Godfather")
for movie_name in movie_names:
movies.append(Movie(name=movie_name, year=random.randint(1970, 2020)))

movies_db = await Movie.objects.using(db).create_many(movies)
for movie, movie_db in zip(movies, movies_db):
assert movie.name == movie_db.name
assert movie.year == movie_db.year
assert isinstance(movie.id, bson.ObjectId)

default_db_movies = await Movie.objects.all()
runtime_db_movies = await Movie.objects.using(db).all()

assert len(default_db_movies) == 0
assert len(runtime_db_movies) == len(movie_names)

class Book(Document):
name: str = mongoz.String()
year: int = mongoz.Integer()

class Meta:
indexes = indexes
database = "test_db"

with pytest.raises(TypeError):
book = Book(name="The Book", year=1972)
movie = Movie(name="Inception", year=2010)
await Movie.objects.using(db).create_many([book, movie]) # type: ignore


@pytest.mark.skipif(sys.version_info < (3, 10), reason="zip() implementation refactored in 3.10+")
async def test_model_bulk_create() -> None:
movies = []
Expand Down

0 comments on commit 25707e1

Please sign in to comment.