> For the complete documentation index, see [llms.txt](https://oppenheimer.gitbook.io/mongoclass/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://oppenheimer.gitbook.io/mongoclass/mongoclass.md).

# Mongoclass

## Installation

To get started, install mongoclass using pip like so.

```
pip install -U mongoclass
```

## Getting Started

This section will explain the basics of how to use mongoclass. After reading this, read the API Reference for more information.

```python
from mongoclass import MongoClassClient
client = MongoClassClient("mongoclass", "localhost:27017")
```

This will create a `MongoClassClient` instance that exposes the features of **mongoclass**. `MongoClassClient` inherits from `pymongo.MongoClient` so you can also use it like you'd normally use pymongo.

### Schemas

To create a schema (or a preferred term, mongoclass), this is all you have to do.

```python
from dataclasses import dataclass

@client.mongoclass()
@dataclass
class User:
    name: str
    email: str
    phone: int
    country: str = "US"
```

{% hint style="info" %}
It is important that `@client.mongoclass()` comes first before the `@dataclass` decorator.
{% endhint %}

This creates a `User` mongoclass that belongs in the `user` collection inside the default database. To create an actual `User` object and have it be inserted in the database, create an instance of `User` and call the `.insert()` method or `.save()` like so.

{% hint style="info" %}
The difference between `.save()` and `.insert()` will be clarified in the **Updating Data** section below.
{% endhint %}

```python
john = User("John Dee", "johndee@gmail.com", 5821)
insert_result = john.insert() # Works!
insert_result = john.save() # Also works!
```

The first line creates the user John Dee with the provided information. Notice how we didn't need to provide a country, that is because country defaults to US.

The second line inserts it to the `user` collection in the default database and then returns a `pymongo.InsertOneResult`

{% hint style="info" %}
As an alternative to having to call `.insert()`, you can pass `_insert=True` to `User()` which will automatically insert as soon as the object is initialized. You do loose the ability to receive the `pymongo.InsertOneResult`
{% endhint %}

What if you don't want to call `insert()` or pass `_insert=True`? Simple, when decorating a dataclass just pass `insert_on_init=True` like so.

```python
@client.mongoclass(insert_on_init=True)
@dataclass
class User:
    ...

john = ("John Dee", "johndee@gmail.com", 5821)
# John is immediately inserted
```

Now what if you want to insert multiple objects? Use the `client.insert_classes` method.

```python
users = [
    User("John Dee", "johndee@gmail.com", 100),
    User("Michael Reeves", "michaelreeves@gmail.com", 42069)
]
client.insert_classes(users)
```

{% hint style="info" %}
What if all the objects in the `users` list do not belong to the same collection and database? Will it insert them in their respective collections and databases?&#x20;
{% endhint %}

Yes, you have to pass `insert_one=True` onto `client.insert_classes` in order for them to be inserted into their respective collections and databases. Under the hood, `insert_one=True` will tell the method to just call the `insert()` method on each object in the list.

### Nested Mongoclasses

Support for nested mongoclasses was added on **v0.4**&#x20;

By default nested is disabled for performance reasons. In order to enabled nested classes, pass `nested=True` onto the mongoclass decorator.

#### Example

```python
@client.mongoclass()
@dataclass
class Metadata:
    age: int
    phone: int


@client.mongoclass(nested=True)
@dataclass
class User:
    name: str
    email: str
    metadata: Metadata

User("John Howards", "johnhowards@gmail.com", Metadata(23, 43123)).insert()
```

Once inserted in the database, the format is changed a bit so that it know how to reconstruct it back into a object once you query for it. Here's how the new format would look like.

```javascript
{
    "name": "John Howards",
    "email": "johnhowards@gmail.com",
    "metadata": {
        "_nest_collection": "metadata",
        "_nest_database": "main",
        "data": {
            "age": 23,
            "phone": 43123
        }
    }
}
```

{% hint style="info" %}
You only need to make the parent class nested. If there are childs with other childs, you don't need to make them nested anymore.
{% endhint %}

You don't need to do anything different when it comes to querying nested data. Just use the `find_class` or `find_classes` normally and it would automatically nest it. Check **Finding Data** section below for more information.

### Mongoclass in a different collection

Now what if you want the `User` object to belong in a different collection and database? It's simple, you just provide some extra arguments to the `mongoclass()` wrapper.

```python
@client.mongoclass("profile", "profile_list")
@dataclass
class User:
    name: str
    email: str
    phone: int
    country: str = "US"
```

This will create a `User` schema that belongs to the `profile` collection instead of `user` inside the `profile_list` database. By default, collections are chosen by the name of the decorated dataclass but lowered.

The second argument can also be a `pymongo.Database` instance if you prefer that.

### Updating Data

Any dataclass that you decorate with `@client.mongoclass()` will have a `update()` and `.save()` method that basically allows you to update that specific object.&#x20;

First, here's an example of how you would update an object or a document using `.update()` that has been already been inserted.

```python
john = User("John Dee", "johndee@gmail.com", 5821)
john.insert()

# Let's change john's country to UK
update_result, new_john = john.update(
        {"$set": {"country": "UK"}}, 
        return_new=True
    )
```

This will update john's country to UK. As you can see, the update method simply accepts the same parameters as pymongo's `update_one()`. The `return_new` means it'll return the new object rather than returning the same object.

Here's an example using the `.save()` method.

```python
# For better example sake, let's imagine we added an age attribute (The last one)
john = User("John Dee", "johndee@gmail.com", 5821, 21)
john.insert()

# Let's change john's country to UK
john.name = "UK"

# Let's also change john's age to 22 by incrementing it
john.age += 1

# Now let's update this entry in the database using the .save()
update_result, new_john = john.save()
```

As you can see, the difference is, `.save()` will basically update based on the current state of the attributes of the object.

### Finding Data

To query data, mongoclass has two methods for that. `client.find_class` and `client.find_classes`

They're kind of like pymongo's `find()` and `find_one()` except they return the mongoclasses that contains the data instead of just a raw document.&#x20;

```python
# Find john in the default database inside the user collection
john = client.find_class("user", {"name": "John Dee"})
```

The first argument points to the collection john belongs in. The second argument is the filter to use. (As same as `find_one()`) Optionally, you can pass a `database="database_name"` parameter to tell mongoclass what database to look at.

{% hint style="warning" %}
If you've overwritten the collection name in `@client.mongoclass()` with something different, the collection you pass must be the same.
{% endhint %}

What if we're looking for multiple data?

```python
us_residents = client.find_classes("user", {"country": "US"})
```

This will return a cursor object similar to pymongo's cursor.  Each data in the cursor has a country  of **US**.

### Deleting

Deleting is simple. You just have to call the `.delete()` method of a mongoclass like so.

```python
delete_result = john.delete()
# Deletes john
```
