Refactor Database class: add try/catch, extract getQueryRandomIds, add parameters
This commit is contained in:
parent
eaa86fd709
commit
22fec9bd43
191
src/database.cpp
191
src/database.cpp
|
|
@ -32,52 +32,67 @@ void Database::ensureTableExists() {
|
||||||
json Database::getRandomRecord() {
|
json Database::getRandomRecord() {
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
|
|
||||||
pqxx::work txn(*conn);
|
try {
|
||||||
|
// Получаем общее количество записей
|
||||||
|
int totalCount = getTotalCount();
|
||||||
|
|
||||||
// Получаем общее количество записей
|
if (totalCount == 0) {
|
||||||
int totalCount = getTotalCount();
|
return json{
|
||||||
|
{"error", "No records found in database"}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (totalCount == 0) {
|
// Генерируем случайный ID
|
||||||
|
std::uniform_int_distribution<int> idDist(1, totalCount);
|
||||||
|
int randomId = idDist(rng);
|
||||||
|
|
||||||
|
pqxx::work txn(*conn);
|
||||||
|
|
||||||
|
// Запрашиваем случайную запись
|
||||||
|
auto result = txn.exec_params(
|
||||||
|
"SELECT id, name, value, description, created_at "
|
||||||
|
"FROM random_data "
|
||||||
|
"WHERE id = $1",
|
||||||
|
randomId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.empty()) {
|
||||||
|
// Если не нашли, берем первую (на всякий случай)
|
||||||
|
result = txn.exec("SELECT id, name, value, description, created_at FROM random_data LIMIT 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.empty()) {
|
||||||
|
txn.abort();
|
||||||
|
return json{
|
||||||
|
{"error", "No records found in database"}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& row = result[0];
|
||||||
|
|
||||||
|
json response = {
|
||||||
|
{"id", row[0].as<int>()},
|
||||||
|
{"name", row[1].as<std::string>()},
|
||||||
|
{"value", row[2].as<int>()},
|
||||||
|
{"description", row[3].as<std::string>()},
|
||||||
|
{"created_at", row[4].as<std::string>()},
|
||||||
|
{"timestamp", std::chrono::system_clock::now().time_since_epoch().count()}
|
||||||
|
};
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
return response;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
return json{
|
return json{
|
||||||
{"error", "No records found in database"}
|
{"error", "Database error in getRandomRecord"},
|
||||||
|
{"message", e.what()}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Генерируем случайный ID
|
|
||||||
std::uniform_int_distribution<int> idDist(1, totalCount);
|
|
||||||
int randomId = idDist(rng);
|
|
||||||
|
|
||||||
// Запрашиваем случайную запись
|
|
||||||
auto result = txn.exec_params(
|
|
||||||
"SELECT id, name, value, description, created_at "
|
|
||||||
"FROM random_data "
|
|
||||||
"WHERE id = $1",
|
|
||||||
randomId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.empty()) {
|
|
||||||
// Если не нашли, берем первую (на всякий случай)
|
|
||||||
result = txn.exec("SELECT id, name, value, description, created_at FROM random_data LIMIT 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& row = result[0];
|
|
||||||
|
|
||||||
json response = {
|
|
||||||
{"id", row[0].as<int>()},
|
|
||||||
{"name", row[1].as<std::string>()},
|
|
||||||
{"value", row[2].as<int>()},
|
|
||||||
{"description", row[3].as<std::string>()},
|
|
||||||
{"created_at", row[4].as<std::string>()},
|
|
||||||
{"timestamp", std::chrono::system_clock::now().time_since_epoch().count()}
|
|
||||||
};
|
|
||||||
|
|
||||||
txn.commit();
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// БЫСТРЫЙ ВАРИАНТ: Использует ORDER BY RANDOM() LIMIT
|
// БЫСТРЫЙ ВАРИАНТ: Использует рекурсивный CTE
|
||||||
|
|
||||||
json Database::getRandomRecordsFast(int count) {
|
json Database::getRandomRecordsFast(const int count) {
|
||||||
pqxx::work txn(*conn);
|
pqxx::work txn(*conn);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -172,41 +187,17 @@ json Database::getRandomRecordsFast(int count) {
|
||||||
|
|
||||||
// МЕДЛЕННЫЙ ВАРИАНТ: Генерирует ID на клиенте, затем запрашивает по IN
|
// МЕДЛЕННЫЙ ВАРИАНТ: Генерирует ID на клиенте, затем запрашивает по IN
|
||||||
|
|
||||||
json Database::getRandomRecordsSlow(int count) {
|
json Database::getRandomRecordsSlow(const int count, const int totalCount) {
|
||||||
// Шаг 1: Получаем общее количество записей с блокировкой
|
// Генерируем уникальные случайные ID
|
||||||
int totalCount = 0;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
|
||||||
|
|
||||||
pqxx::work txn(*conn);
|
|
||||||
auto countResult = txn.exec("SELECT COUNT(*) FROM random_data");
|
|
||||||
totalCount = countResult[0][0].as<int>();
|
|
||||||
txn.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalCount == 0) {
|
|
||||||
return json{
|
|
||||||
{"error", "No records found in database"},
|
|
||||||
{"count", 0}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > totalCount) {
|
|
||||||
count = totalCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Шаг 2: Генерируем уникальные случайные ID
|
|
||||||
std::set<int> ids;
|
std::set<int> ids;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
|
||||||
std::uniform_int_distribution<int> idDist(1, totalCount);
|
std::uniform_int_distribution<int> idDist(1, totalCount);
|
||||||
|
|
||||||
while (ids.size() < static_cast<size_t>(count)) {
|
while (ids.size() < static_cast<size_t>(count)) {
|
||||||
ids.insert(idDist(rng));
|
ids.insert(idDist(rng));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Шаг 3: Запрашиваем записи по сгенерированным ID
|
// Запрашиваем записи по сгенерированным ID
|
||||||
pqxx::work txn(*conn);
|
pqxx::work txn(*conn);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -219,17 +210,7 @@ json Database::getRandomRecordsSlow(int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Формируем запрос с IN
|
// Формируем запрос с IN
|
||||||
std::string query =
|
std::string query = getQueryRandomIds(static_cast<int>(ids.size()));
|
||||||
"SELECT id, name, value, description, created_at "
|
|
||||||
"FROM random_data WHERE id IN (";
|
|
||||||
|
|
||||||
// Подготавливаем параметры
|
|
||||||
std::vector<std::string> placeholders;
|
|
||||||
for (size_t i = 0; i < ids.size(); ++i) {
|
|
||||||
if (i > 0) query += ",";
|
|
||||||
query += "$" + std::to_string(i + 1);
|
|
||||||
}
|
|
||||||
query += ") ORDER BY id";
|
|
||||||
|
|
||||||
// Подготавливаем параметры для exec_params
|
// Подготавливаем параметры для exec_params
|
||||||
pqxx::params param_list;
|
pqxx::params param_list;
|
||||||
|
|
@ -270,7 +251,8 @@ json Database::getRandomRecordsSlow(int count) {
|
||||||
|
|
||||||
// ГЛАВНАЯ ФУНКЦИЯ: Выбирает стратегию в зависимости от count
|
// ГЛАВНАЯ ФУНКЦИЯ: Выбирает стратегию в зависимости от count
|
||||||
|
|
||||||
json Database::getRandomRecords(int count) {
|
json Database::getRandomRecords(int count, int med, int max) {
|
||||||
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
// Валидация входных параметров
|
// Валидация входных параметров
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
return json{
|
return json{
|
||||||
|
|
@ -278,36 +260,41 @@ json Database::getRandomRecords(int count) {
|
||||||
{"count", 0}
|
{"count", 0}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// Получаем общее количество записей с блокировкой
|
||||||
|
int totalCount = getTotalCount();
|
||||||
|
|
||||||
|
if (totalCount == 0) {
|
||||||
|
return json{
|
||||||
|
{"error", "No records found in database"},
|
||||||
|
{"count", 0}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > totalCount) {
|
||||||
|
count = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
// Защита от слишком больших запросов
|
// Защита от слишком больших запросов
|
||||||
const int MAX_RECORDS = 1000;
|
if (count > max) {
|
||||||
if (count > MAX_RECORDS) {
|
count = max;
|
||||||
count = MAX_RECORDS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Выбор стратегии:
|
// Выбор стратегии:
|
||||||
// - Для малых выборок (<= 10) используем медленный, но более точный способ
|
// - Для малых выборок (<= med) используем медленный, но более точный способ
|
||||||
// - Для больших выборок (> 10) используем быстрый способ с рекурсивным CTE запросом
|
// - Для больших выборок (> med) используем быстрый способ с рекурсивным CTE запросом
|
||||||
const int SLOW_STRATEGY_LIMIT = 10;
|
|
||||||
|
|
||||||
json response;
|
json response;
|
||||||
if (count <= SLOW_STRATEGY_LIMIT) {
|
if (count <= med) {
|
||||||
response = getRandomRecordsSlow(count);
|
response = getRandomRecordsSlow(count, totalCount);
|
||||||
// Добавляем информацию о стратегии в ответ
|
|
||||||
response["strategy"] = "slow";
|
|
||||||
response["strategy_reason"] = "count <= " + std::to_string(SLOW_STRATEGY_LIMIT) +
|
|
||||||
" (more precise random distribution)";
|
|
||||||
} else {
|
} else {
|
||||||
response = getRandomRecordsFast(count);
|
response = getRandomRecordsFast(count);
|
||||||
// Добавляем информацию о стратегии в ответ
|
|
||||||
response["strategy"] = "fast";
|
|
||||||
response["strategy_reason"] = "count > " + std::to_string(SLOW_STRATEGY_LIMIT) +
|
|
||||||
" (better performance for large datasets)";
|
|
||||||
}
|
}
|
||||||
|
if(response.contains("error")){
|
||||||
|
return response;
|
||||||
|
}
|
||||||
// Добавляем общую мета-информацию
|
// Добавляем общую мета-информацию
|
||||||
response["requested_count"] = count;
|
response["requested_count"] = count;
|
||||||
response["total_available"] = getTotalCount();
|
response["total_available"] = totalCount;
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
@ -320,3 +307,17 @@ int Database::getTotalCount() {
|
||||||
txn.commit();
|
txn.commit();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Database::getQueryRandomIds(const int count){
|
||||||
|
std::string query =
|
||||||
|
"SELECT id, name, value, description, created_at "
|
||||||
|
"FROM random_data WHERE id IN (";
|
||||||
|
|
||||||
|
// Подготавливаем параметры
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
if (i > 0) query += ",";
|
||||||
|
query += "$" + std::to_string(i + 1);
|
||||||
|
}
|
||||||
|
query += ") ORDER BY id";
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ public:
|
||||||
~Database() = default;
|
~Database() = default;
|
||||||
|
|
||||||
json getRandomRecord();
|
json getRandomRecord();
|
||||||
json getRandomRecords(int count = 5);
|
json getRandomRecords(int count, int med, int max);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<pqxx::connection> conn;
|
std::unique_ptr<pqxx::connection> conn;
|
||||||
|
|
@ -28,7 +28,8 @@ private:
|
||||||
|
|
||||||
int getTotalCount();
|
int getTotalCount();
|
||||||
// Две стратегии получения случайных записей
|
// Две стратегии получения случайных записей
|
||||||
json getRandomRecordsFast(int count);
|
json getRandomRecordsFast(const int count);
|
||||||
json getRandomRecordsSlow(int count);
|
json getRandomRecordsSlow(const int count, const int totalCount);
|
||||||
|
std::string getQueryRandomIds(const int count);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue