Metadata-Version: 2.4
Name: vrbase
Version: 2.0.0
Summary: Русская документо-ориентированная база данных — проще чем SQL
Author: Pantapan1
Keywords: база данных,database,json,русский,documents,nosql
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Provides-Extra: crypto
Requires-Dist: cryptography>=42.0.0; extra == "crypto"
Dynamic: author
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: keywords
Dynamic: provides-extra
Dynamic: requires-python
Dynamic: summary

VRBase — Русская база данных

Документо-ориентированная база данных на русском языке. Проще чем SQL, быстрее чем JSON вручную.

https://img.shields.io/pypi/v/vrbase.svg
https://img.shields.io/badge/python-3.8+-blue.svg
https://img.shields.io/badge/license-MIT-green.svg

---

📖 Оглавление

· Почему VRBase
· Установка
· Быстрый старт
· Основные понятия
· Полное руководство
  · База данных
  · Коробка
  · Документы
  · Поиск
  · Обновление
  · Удаление
  · Индексы
  · Запросы
  · Агрегация
  · Шифрование
  · Миграции
· Сравнение с SQL
· Примеры
· API Reference

---

Почему VRBase

Проблема SQL

```sql
CREATE TABLE players (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    level INTEGER DEFAULT 1,
    inventory TEXT,
    stats TEXT
);

INSERT INTO players (name, level, inventory, stats) 
VALUES ('Миро', 10, '["меч","щит"]', '{"hp":100,"mp":50}');

SELECT * FROM players WHERE level > 5 ORDER BY level DESC LIMIT 10;
```

Решение VRBase

```python
from vrbase import База

мир = База("survival")
игроки = мир.коробка("игроки")

игроки.положить({
    "имя": "Миро",
    "уровень": 10,
    "инвентарь": ["меч", "щит"],
    "статы": {"здоровье": 100, "мана": 50}
})

топ = игроки.найти(где={"уровень": "> 5"}, сортировка="уровень убыв", лимит=10)
```

Разница: Не нужно думать о схемах, типах данных, JOIN'ах, миграциях. Просто кладёшь словарь и ищешь по-русски.

---

Установка

```bash
pip install vrbase
```

Или из исходников:

```bash
git clone https://github.com/Pantapan1/VRBase.git
cd VRBase
pip install -e .
```

Зависимости: cryptography>=41.0.0 (только для шифрования)

---

Быстрый старт

```python
from vrbase import База

# Создаём или открываем базу
мир = База("мой_мир")

# Создаём коробку (аналог таблицы)
игроки = мир.коробка("игроки")

# Добавляем игрока
игроки.положить({
    "имя": "Миро",
    "уровень": 1,
    "класс": "воин",
    "инвентарь": ["меч", "зелье"],
    "статы": {"здоровье": 100, "мана": 50}
})

# Ищем
новички = игроки.найти(где={"уровень": "< 5"})

# Обновляем
игроки.обновить(где={"имя": "Миро"}, данные={"уровень": 10})

# Сохраняем
мир.сохранить()

print(f"Игроков: {игроки.считать()}")
```

---

Основные понятия

VRBase SQL
База Database
Коробка Table
Документ Row
Ключ Column
положить() INSERT
найти() SELECT
обновить() UPDATE
удалить() DELETE
Индекс Index

---

Полное руководство

База данных

```python
from vrbase import База

# Обычная база (файл .json)
мир = База("survival")

# База с шифрованием (.enc)
мир = База("survival", ключ="мой_секретный_пароль")

# База в конкретной папке
мир = База("survival", путь="/path/to/survival.json")

# Контекстный менеджер (авто-сохранение)
with База("survival") as мир:
    игроки = мир.коробка("игроки")
    игроки.положить({"имя": "Миро"})
# Автоматически сохранится

# Просмотр информации
print(мир.инфо())
# {'имя': 'survival', 'коробок': 1, 'документов': 1, 'размер_байт': 256}

# Бэкап
мир.бэкап()  # → survival_2026-05-09_12-30-00.json

# Сохранить копию
мир.сохранить_как("survival_backup.json")

# Очистить
мир.очистить()  # Удаляет всё
```

Коробка

```python
игроки = мир.коробка("игроки")

# Сколько документов
print(len(игроки))        # 5
print(игроки.размер())    # 5
print(игроки.память())    # 2500 (байт в памяти)

# Все ключи документов
print(игроки.ключи())     # ['_id', '_создан', 'имя', 'уровень', ...]

# Уникальные значения поля
print(игроки.уникальные("класс"))  # ['воин', 'маг', 'вор']

# Есть ли документ с таким условием
if игроки.есть(где={"имя": "Миро"}):
    print("Миро существует")

# Перебор
for игрок in игроки:
    print(игрок['имя'])

# Доступ по ID
игрок = игроки[0]  # Первый документ
```

Документы

```python
# Добавить один
ид = игроки.положить({
    "имя": "Миро",
    "уровень": 1,
    "инвентарь": ["меч", "хлеб"],
    "статы": {"здоровье": 100, "выносливость": 80}
})
print(ид)  # 0

# Добавить много
игроки.положить_много([
    {"имя": "Кай", "уровень": 5},
    {"имя": "Анна", "уровень": 7},
    {"имя": "Олег", "уровень": 3}
])

# Автоматически добавляются поля:
# _id — уникальный номер
# _создан — время создания в ISO формате
# _обновлён — время последнего обновления

игрок = игроки.найти_один(где={"имя": "Миро"})
print(игрок['_id'])        # 0
print(игрок['_создан'])    # 2026-05-09T12:30:00
```

Поиск

```python
# Точное совпадение
игроки.найти(где={"уровень": 10})

# Больше / меньше
игроки.найти(где={"уровень": "> 5"})
игроки.найти(где={"уровень": ">= 10"})
игроки.найти(где={"уровень": "< 3"})
игроки.найти(где={"уровень": "!= 5"})

# Диапазон
игроки.найти(где={"уровень": "между 5 и 20"})

# Поиск подстроки
игроки.найти(где={"имя": "содержит мир"})  # найдёт "Миро", "Владимир"

# Начинается с / заканчивается на
игроки.найти(где={"имя": "начинается с М"})
игроки.найти(где={"имя": "заканчивается на о"})

# Пусто / не пусто
игроки.найти(где={"инвентарь": "пусто"})
игроки.найти(где={"инвентарь": "не пусто"})

# Поиск по вложенному полю
игроки.найти(где={"статы.здоровье": "> 50"})

# Содержит в списке
игроки.найти(содержит={"инвентарь": "меч"})

# Сортировка
игроки.найти(сортировка="уровень убыв")
игроки.найти(сортировка="имя возр")

# Комбинированный поиск
ветераны = игроки.найти(
    где={"уровень": "> 10", "класс": "воин"},
    сортировка="уровень убыв",
    лимит=5
)

# Найти один
миро = игроки.найти_один(имя="Миро")

# Найти по ID
игрок = игроки.найти_по_id(0)
```

Обновление

```python
# Обновить по условию
игроки.обновить(
    где={"имя": "Миро"},
    данные={"уровень": 15, "золото": 500}
)

# Обновить по ID
игроки.обновить_по_id(0, {"уровень": 20})

# Обновить все документы
игроки.обновить_все({"бонус": 0})

# Обновить вложенное поле
игроки.обновить(
    где={"имя": "Миро"},
    данные={"статы.здоровье": 150}
)

# Авто-обновление _обновлён
игрок = игроки.найти_один(имя="Миро")
print(игрок['_обновлён'])  # 2026-05-09T13:00:00
```

Удаление

```python
# Удалить по условию
игроки.удалить(где={"уровень": "< 2"})

# Удалить по ID
игроки.удалить_по_id(0)

# Удалить всё
игроки.удалить_все()
```

Индексы

Индексы ускоряют поиск в 10-1000 раз на больших объёмах данных.

```python
# Создать индекс
игроки.создать_индекс("имя")
игроки.создать_индекс("уровень")

# Составной индекс
игроки.создать_индекс(["класс", "уровень"])

# Поиск с индексом (автоматически используется)
игроки.найти(где={"имя": "Миро"})  # O(log n) вместо O(n)

# Удалить индекс
игроки.удалить_индекс("имя")

# Перестроить все индексы
игроки.перестроить_индексы()

# Статистика индекса
индекс = игроки.создать_индекс("уровень")
print(индекс.статистика())
# {'тип': 'числовой', 'уникальных': 15, 'минимум': 1, 'максимум': 50, 'среднее': 12.3}
```

Запросы

Сложные запросы через конструктор:

```python
from vrbase import Запрос

# Сложный запрос
результат = (Запрос(игроки)
    .где(уровень="> 5")
    .и_(класс="воин")
    .или(класс="маг")
    .не_(статус="мёртв")
    .содержит(инвентарь="меч")
    .сортировать_по("уровень", убывание=True)
    .лимит(10)
    .пропустить(5)
    .выполнить())

# Выбрать только определённые поля
результат = (Запрос(игроки)
    .выбрать("имя", "уровень")
    .где(уровень="> 10")
    .выполнить())

# Уникальные значения
результат = (Запрос(игроки)
    .выбрать("класс")
    .уникальные()
    .выполнить())

# Проверить существование
есть = (Запрос(игроки)
    .где(имя="Миро")
    .существует())

# Парсинг строки запроса
from vrbase import ПарсерЗапроса

запрос = ПарсерЗапроса.разобрать(
    "выбрать имя, уровень где уровень > 5 сортировка по уровень лимит 5",
    игроки
)
результат = запрос.выполнить()
```

Агрегация

```python
# Подсчёт
стата = (игроки.агрегация()
    .считать()
    .выполнить())
# [{'количество': 150}]

# Группировка
стата = (игроки.агрегация()
    .группировать_по("класс")
    .считать()
    .среднее("уровень")
    .выполнить())
# [
#   {'класс': 'воин', 'количество': 50, 'среднее_уровень': 12.3},
#   {'класс': 'маг', 'количество': 30, 'среднее_уровень': 15.1}
# ]

# Сумма, минимум, максимум
стата = (игроки.агрегация()
    .группировать_по("класс")
    .считать("игроков")
    .сумма("золото", "всего_золота")
    .максимум("уровень", "макс_уровень")
    .выполнить())
```

Шифрование

```python
from vrbase import Шифр

# Зашифровать данные
зашифровано = Шифр.зашифровать("секретные данные", "мой_пароль")

# Расшифровать
данные = Шифр.расшифровать(зашифровано, "мой_пароль")

# Зашифровать файл
Шифр.зашифровать_файл("data.json", "пароль", "data.enc")

# Расшифровать файл
Шифр.расшифровать_файл("data.enc", "пароль", "data.json")

# Хэш строки
хэш = Шифр.хэш("важные данные")

# Хэш файла
хэш = Шифр.хэш_файла("data.json")

# Сравнить файлы
одинаковые = Шифр.сравнить_хэши("file1.json", "file2.json")

# Электронная подпись
подпись = Шифр.подписать("документ", "ключ")
валидна = Шифр.проверить_подпись("документ", подпись, "ключ")

# Сгенерировать пароль
пароль = Шифр.сгенерировать_пароль(16)

# Проверить надёжность пароля
print(Шифр.надёжность_пароля("qwerty"))        # Слабый
print(Шифр.надёжность_пароля("Qw3rty!@2026"))  # Очень надёжный
```

Миграции

```python
from vrbase import Миграция, МенеджерМиграций

менеджер = МенеджерМиграций(мир)

# Создать миграцию
v1 = (Миграция("1.0.0", "Начальная структура")
    .добавить_поле("игроки", "золото", 100)
    .добавить_поле("игроки", "опыт", 0))

v2 = (Миграция("1.1.0", "Добавляем магию")
    .добавить_поле("игроки", "мана", 50)
    .добавить_поле("игроки", "макс_мана", 50))

v3 = (Миграция("2.0.0", "Переименовываем")
    .переименовать_поле("игроки", "золото", "монеты"))

# Зарегистрировать
менеджер.зарегистрировать(v1)
менеджер.зарегистрировать(v2)
менеджер.зарегистрировать(v3)

# Применить все
менеджер.применить()

# Применить до определённой версии
менеджер.применить(до_версии="1.1.0")

# Статус
print(менеджер.статус())
# {'всего': 3, 'применено': 2, 'ожидают': 1}

# Откатить
менеджер.откатить("1.0.0")
```

---

Сравнение с SQL

Действие SQL VRBase
Создать таблицу CREATE TABLE ... мир.коробка("имя")
Вставить INSERT INTO ... .положить({...})
Выбрать все SELECT * FROM ... .найти_все()
Выбрать с условием WHERE x = 5 .найти(где={"x": 5})
Больше/меньше WHERE x > 5 .найти(где={"x": "> 5"})
Сортировка ORDER BY x DESC сортировка="x убыв"
Лимит LIMIT 10 лимит=10
Обновить UPDATE ... SET ... .обновить(где=..., данные=...)
Удалить DELETE FROM ... .удалить(где=...)
Индекс CREATE INDEX ... .создать_индекс(...)
JOIN LEFT JOIN ... Вложенные документы
Схема Обязательна Не нужна
Миграции ALTER TABLE ... Автоматически

---

Примеры

RPG-бот на RuGram + VRBase

```python
from ядро import РуГрам
from vrbase import База

бот = РуГрам("ТОКЕН")
мир = База("rpg_bot")
игроки = мир.коробка("игроки")

@бот.обработать(команда="start")
def старт(сообщение, контекст):
    игрок = игроки.найти_один(чат_ид=сообщение.чат.ид)
    if not игрок:
        игроки.положить({
            "чат_ид": сообщение.чат.ид,
            "имя": сообщение.от.имя,
            "уровень": 1,
            "здоровье": 100,
            "золото": 0
        })
        мир.сохранить()
    
    бот.отправить(сообщение.чат.ид, "Добро пожаловать!")
```

---

API Reference

База

Метод Описание
База(имя, путь, ключ) Создать/открыть базу
.коробка(имя) Получить коробку
.сохранить() Сохранить на диск
.загрузить() Загрузить с диска
.бэкап() Создать копию
.очистить() Удалить всё
.инфо() Информация о базе
.список_коробок() Список коробок
.размер() Размер файла в байтах

Коробка

Метод Описание
.положить(документ) Добавить документ
.положить_много(список) Добавить много
.найти(где, содержит, сортировка, лимит) Поиск
.найти_один(**условия) Найти один
.найти_все() Все документы
.найти_по_id(ид) По ID
.считать(где) Количество
.обновить(где, данные) Обновить
.обновить_по_id(ид, данные) Обновить по ID
.обновить_все(данные) Обновить все
.удалить(где) Удалить
.удалить_по_id(ид) Удалить по ID
.удалить_все() Очистить
.есть(где) Проверить существование
.создать_индекс(ключ) Создать индекс
.удалить_индекс(ключ) Удалить индекс
.уникальные(ключ) Уникальные значения
.ключи() Все ключи
.экспорт() Экспорт данных
.импорт(данные) Импорт данных
.запрос() Создать запрос
.агрегация() Создать агрегацию

---

Лицензия

MIT License. Свободное использование, модификация и распространение.
