# Storage

> Save data between runs. Variables for small values, databases for tables, Mongo for big or nested data, and locks.

A command forgets everything when it finishes. To keep data, like coins, levels or warnings, save it with one of these.

| Use | For | Example |
|---|---|---|
| `server_var` | One value for the whole server | A counter, a "giveaway is open" flag |
| `member_var` | One value per member | Coins, XP, warnings |
| `temp_var` | A value kept in memory only | A short-lived flag |
| `load_database` | A table you made on the dashboard | A shop, a list of tickets |
| `SlashMongo` | Big or nested data | Inventories, leaderboards, logs |

Not sure which to use? Read [Saving data](https://ccbot.app/docs/concepts/saving-data).

## server_var

Reads a server variable. Returns `default` when the variable does not exist.

```python
server_var(name, default=None)
```

```python
count = int(server_var("counter", 0))
```

> **Variables come back as text:** Saved values are stored as text. `"5" + 1` fails. Turn the value into a number first with `int(...)` or `float(...)`.

You can also see and edit server variables on the dashboard under **Variables**.

## update_server_var

Saves a server variable. Makes it when it does not exist.

```python
update_server_var(name, value)
```

```python
count = int(server_var("counter", 0)) + 1
update_server_var("counter", count)
set_content(f"This command was used {count} times.")
```

## member_var

Reads a variable saved for one member. Returns `default` when it does not exist.

```python
member_var(name, default=None, member=None)
```

With no `member`, it uses the person who ran the command or caused the event.

```python
coins = int(member_var("coins", 0))
set_content(f"You have {coins} coins.")
```

## update_member_var

Saves a variable for one member.

```python
update_member_var(name, value, member=None)
```

```python
coins = int(member_var("coins", 0, member=target))
update_member_var("coins", coins + 100, member=target)
```

## temp_var

Reads a value kept in the bot's memory. Returns `default` when it is not set.

```python
temp_var(name, default=None)
```

> **Note:** Temp values are lost when the bot restarts. A value of `0`, `""` or `False` also gives back `default`.

## set_temp_var

Keeps a value in memory for this server.

```python
set_temp_var(name, value)
```

## load_database

Opens a table you made on the dashboard under **Variables → Databases**.

```python
db = load_database(database_id)
```

The database ID is on the dashboard. A wrong ID stops the script with *"Database not found."*

Each row is a dictionary. It has an `id` and one key per column. Columns you do not fill in get their default value.

### db.find

Returns every row that matches, as a list.

```python
db.find(where=None, sort=None)
```

### db.find_first

Returns the first row that matches, or `None`.

```python
db.find_first(where=None, sort=None)
```

### db.insert

Adds a row. Returns the new row with its `id`.

```python
db.insert(data)
```

### db.update

Changes every row that matches. Returns `{"updated": count}`.

```python
db.update(where, data)
```

### db.delete

Deletes every row that matches. Returns `{"deleted": count}`.

```python
db.delete(where)
```

### Filters

`where` is a dictionary. Give a value to match it exactly, or a dictionary of filters.

| Filter | Matches when the column… |
|---|---|
| `gt`, `gte` | is more than, or at least, the value |
| `lt`, `lte` | is less than, or at most, the value |
| `not` | is not the value |
| `contains`, `not_contains` | has the text or item in it, or not |
| `startswith`, `endswith` | starts or ends with the text |
| `in`, `not_in` | is one of the values in a list, or not |
| `has_all`, `has_any` | is a list that has all, or any, of the values |

`sort` is a dictionary of column names and `"asc"` or `"desc"`.

**Example: a shop**

```python
shop = load_database("shop_items")

cheap = shop.find(where={"price": {"lte": 500}}, sort={"price": "asc"})
set_content(listify([f"{item['name']}: {item['price']} coins" for item in cheap]))

shop.insert({"name": "Golden Hat", "price": 1200})
shop.update({"name": "Golden Hat"}, {"price": 999})
shop.delete({"name": "Golden Hat"})
```

## SlashMongo

Opens a MongoDB collection for this server. Use it for big or nested data. Each server has its own databases, so names never clash with other servers.

```python
col = SlashMongo(db_name, collection_name="main")
```

Every method needs `await`.

| Method | What it does |
|---|---|
| `await col.insert_one(data)` | Adds one document. |
| `await col.insert_many(list)` | Adds many documents. |
| `await col.find_one(filter)` | Returns the first match, or `None`. |
| `await col.find(filter)` | Returns every match, as a list. |
| `await col.update_one(filter, change)` | Changes the first match. |
| `await col.update_many(filter, change)` | Changes every match. |
| `await col.delete_one(filter)` | Deletes the first match. |
| `await col.delete_many(filter)` | Deletes every match. |
| `await col.count(filter=None)` | Counts matches. With no filter, counts all. |
| `await col.aggregate(pipeline)` | Runs a MongoDB aggregation. |

Filters and changes use normal MongoDB syntax, like `{"$inc": {"coins": 10}}`.

**Example: an inventory**

```python
inv = SlashMongo("economy", "inventory")

await inv.update_one(
    {"user_id": str(user.id)},
    {"$push": {"items": "Golden Hat"}},
    upsert=True,
)

doc = await inv.find_one({"user_id": str(user.id)})
set_content(f"Your items: {', '.join(doc['items'])}")
```

`find` and `aggregate` return at most **10,000** documents on free servers and **500,000** on premium.

You can look at Mongo data on the dashboard under **Mongo Databases**.

## acquire_lock

Stops the same thing running twice at once. When the lock is already taken, the script stops with an error.

```python
acquire_lock(key, count=1, error=None)
```

| Argument | What it does |
|---|---|
| `key` | A name for the lock. |
| `count` | How many runs may hold it at the same time. |
| `error` | The message to show when the lock is taken. |

## release_lock

Frees a lock so the next run can take it.

```python
release_lock(key)
```

**Example: one purchase at a time per member**

```python
key = f"buy-{user.id}"
acquire_lock(key, error="Your last purchase is still going. Wait a moment.")
coins = int(member_var("coins", 0))
update_member_var("coins", coins - 100)
release_lock(key)
```

> **Watch out:** Always release the lock. When the script stops between the two calls, for example with `set_error`, the lock stays taken until the bot restarts.
