Легковесный JSON БД фреймворк.
• Добавлена автоматическая детекция типов связей • Реализован Proxy-доступ к связям через свойства • Поддержка каскадного удаления данных • Добавлены транзакции с откатом состояния • Обновлена документация и примеры использования Миграция: - Вместо методов xown()/shared() используйте прямое присваивание - Методы getXown() заменены на доступ через свойства |
||
|---|---|---|
| .idea | ||
| greenbean.js | ||
| package.json | ||
| README.md | ||
GreenBean Документация
🌱 Введение
GreenBean — это современная ORM для Node.js, сочетающая простоту RedBeanPHP с мощью JavaScript. Работает поверх JSON-хранилища, идеально подходит для прототипов, MVP и приложений с гибкой схемой данных.
Ключевые концепции:
- Динамическая схема — не требует предварительного описания моделей
- Живые объекты — изменения сохраняются автоматически
- Соглашения вместо конфигурации — автоматическое разрешение связей
- Транзакционный подход — атомарные операции изменения данных
🛠 Установка
npm install greenbean uuid
🎯 Быстрый старт
Создание простого блога
import GreenBean from 'greenbean';
// 1. Инициализация
const gb = new GreenBean('blog.json', {
autoSave: true // Автосохранение при изменениях
});
// 2. Создание сущностей
const author = await gb.dispense('Author', {
name: 'Анна',
bio: 'JS разработчик из Москвы'
});
const post = await gb.dispense('Post', {
title: 'Мой первый пост',
content: '...'
});
// 3. Установка связей
post.author = author; // Один-ко-многим
post.tags = [ // Многие-ко-многим
await gb.dispense('Tag', {name: 'программирование'}),
await gb.dispense('Tag', {name: 'учебник'})
];
// 4. Добавление комментариев (Один-ко-многим)
post.comments = [
await gb.dispense('Comment', {text: 'Отличный пост!'}),
await gb.dispense('Comment', {text: 'Спасибо за статью'})
];
// 5. Получение связанных данных
const loadedPost = await gb.load('Post', post.id);
console.log('Автор:', (await loadedPost.author).name);
console.log('Теги:', await loadedPost.tags);
console.log('Комментарии:', await loadedPost.comments);
🔄 Жизненный цикл бина
- Создание —
dispense() - Модификация — прямое изменение свойств
- Сохранение — автоматическое или через
store() - Удаление —
trash()
// Пример жизненного цикла
const user = await gb.dispense('User'); // 1. Создание
user.name = 'Иван'; // 2. Модификация
await gb.store(user); // 3. Сохранение
await gb.trash(user); // 4. Удаление
🤝 Работа со связями
Правила именования
| Тип связи | Паттерн | Пример |
|---|---|---|
| Один-ко-многим | parent.children |
user.posts |
| Многие-ко-многим | parent_relation таблица |
post_tags |
| Один-к-одному | parent.prop + уникальность |
user.profile |
Практические примеры
1. Социальная сеть
// Пользователь и друзья (многие-ко-многим)
const user = await gb.dispense('User');
user.friends = [
await gb.dispense('User', {name: 'Мария'}),
await gb.dispense('User', {name: 'Петр'})
];
// Группы (один-ко-многим)
const group = await gb.dispense('Group');
group.members = [
await gb.dispense('User', {name: 'Анна'}),
await gb.dispense('User', {name: 'Сергей'})
];
2. Электронная коммерция
// Заказ и товары (многие-ко-многим)
const order = await gb.dispense('Order');
order.products = [
await gb.dispense('Product', {name: 'Ноутбук'}),
await gb.dispense('Product', {name: 'Чехол'})
];
// Платежная информация (один-к-одному)
order.payment = await gb.dispense('Payment', {
amount: 999.99,
status: 'completed'
});
🔍 Расширенный поиск
Поддерживаемые операторы
const results = await gb.find('User', {
age: { gt: 18, lt: 30 }, // Больше 18 и меньше 30
name: { like: 'Ив%' }, // Начинается с "Ив"
email: { neq: null }, // Email не null
rating: { between: [4, 5] } // Рейтинг от 4 до 5
});
Сортировка и пагинация
const posts = await gb.find('Post',
{ status: 'published' },
{
limit: 10,
offset: 20,
order: { created: 'DESC' }
}
);
🛡 Транзакции и безопасность
try {
await gb.transaction(async () => {
const product = await gb.dispense('Product');
product.stock -= 1;
const order = await gb.dispense('Order');
order.items = [product];
if (product.stock < 0) {
throw new Error('Нет в наличии');
}
});
} catch (error) {
console.error('Ошибка транзакции:', error.message);
}
🧊 Заморозка данных
// Защита от изменений
gb.freeze();
try {
const user = await gb.dispense('User'); // Ошибка!
} catch (e) {
console.error('База заморожена');
}
// Временная разморозка
gb.freeze(false);
const tempUser = await gb.dispense('User');
gb.freeze(true);
🧩 Лучшие практики
1. Сервисные классы
class UserService {
constructor(gb) {
this.gb = gb;
}
async register(userData) {
const user = await this.gb.dispense('User', userData);
user.verificationToken = crypto.randomBytes(32).toString('hex');
return this.gb.store(user);
}
}
2. Валидация данных
function validatePost(post) {
const errors = [];
if (!post.title || post.title.length < 5) {
errors.push('Название слишком короткое');
}
if (!post.content || post.content.length < 100) {
errors.push('Минимум 100 символов в содержании');
}
return errors;
}
3. Обработка ошибок
async function safeDeleteUser(userId) {
try {
const user = await gb.load('User', userId);
await gb.trash(user);
return true;
} catch (error) {
console.error(`Ошибка удаления пользователя ${userId}:`, error);
return false;
}
}
📚 Полное API
Основные методы
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
dispense(type, props) |
Тип, свойства | Бин | Создает новый объект |
store(bean) |
Бин | ID | Сохраняет изменения |
load(type, id) |
Тип, ID | Бин/null | Загружает по ID |
find(type, criteria) |
Тип, критерии | Массив бинов | Поиск с фильтрацией |
trash(bean) |
Бин | void | Удаляет объект и связи |
Настройки
new GreenBean('data.json', {
autoSave: false, // Автоматическое сохранение
freeze: false, // Начальное состояние блокировки
cacheTTL: 300000 // Время жизни кеша (5 минут)
});
📜 Лицензия
MIT License © 2025 Rockzo development