opex792 commited on
Commit
c05d5c9
·
verified ·
1 Parent(s): 64e4423

Upload database.js

Browse files
Files changed (1) hide show
  1. database.js +55 -40
database.js CHANGED
@@ -17,9 +17,8 @@ export async function initializeDatabase() {
17
  console.log('Подключение к SQLite (Main API) установлено.');
18
 
19
  await db.exec('PRAGMA journal_mode = WAL;');
20
- await db.exec('PRAGMA synchronous = NORMAL;'); // Немного менее безопасно, но значительно быстрее для массовых записей
21
 
22
- // Создаем основные таблицы
23
  await db.exec(`
24
  CREATE TABLE IF NOT EXISTS movies (
25
  id INTEGER PRIMARY KEY,
@@ -52,35 +51,38 @@ export async function initializeDatabase() {
52
  }
53
 
54
  /**
55
- * Полностью переработанная, быстрая и надежная функция загрузки данных.
 
56
  */
57
  export async function refreshData() {
58
  console.log('Начинается высокопроизводительная перезагрузка данных...');
59
  try {
60
- const response = await fetch(DATA_URL);
61
- if (!response.ok) throw new Error(`Ошибка загрузки: ${response.statusText}`);
62
-
63
- const gunzip = createGunzip();
64
- response.body.pipe(gunzip);
65
- const rl = createInterface({ input: gunzip, crlfDelay: Infinity });
66
-
67
- // --- ШАГ 1: Чтение файла и сбор данных в память ---
68
- console.log('Шаг 1: Чтение файла и агрегация данных в памяти...');
69
- const moviesData = [];
70
  const allGenres = new Set();
71
  const allCountries = new Set();
72
- for await (const line of rl) {
 
73
  if (line.trim()) {
 
74
  const movie = JSON.parse(line);
75
- moviesData.push(movie);
76
  movie.genres?.forEach(g => allGenres.add(g.name));
77
  movie.countries?.forEach(c => allCountries.add(c.name));
78
  }
79
  }
80
- console.log(`Шаг 1 завершен. Найдено ${moviesData.length} фильмов, ${allGenres.size} уникальных жанров, ${allCountries.size} уникальных стран.`);
81
 
 
82
  await db.exec('BEGIN TRANSACTION;');
83
- // Очищаем таблицы
 
84
  await db.exec('DELETE FROM movie_genres;');
85
  await db.exec('DELETE FROM movie_countries;');
86
  await db.exec('DELETE FROM genres;');
@@ -102,7 +104,7 @@ export async function refreshData() {
102
  await countryStmt.finalize();
103
  console.log('Шаг 2 завершен.');
104
 
105
- // --- ШАГ 3: Кэширование ID жанров и стран ---
106
  console.log('Шаг 3: Кэширование ID в память...');
107
  const genreCache = new Map();
108
  const countryCache = new Map();
@@ -112,34 +114,44 @@ export async function refreshData() {
112
  countriesFromDb.forEach(c => countryCache.set(c.name, c.id));
113
  console.log('Шаг 3 завершен.');
114
 
115
- // --- ШАГ 4: Пакетная вставка фильмов и связей ---
116
- console.log('Шаг 4: Пакетная запись фильмов и связей...');
 
 
 
 
 
 
 
117
  const movieStmt = await db.prepare('INSERT INTO movies (id, year, type, rating_kp, rating_imdb, age_rating, movie_length, is_series, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
118
- // ИСПРАВЛЕНИЕ: Используем INSERT OR IGNORE для избежания сбоя при дубликатах
119
  const movieGenreStmt = await db.prepare('INSERT OR IGNORE INTO movie_genres (movie_id, genre_id) VALUES (?, ?)');
120
  const movieCountryStmt = await db.prepare('INSERT OR IGNORE INTO movie_countries (movie_id, country_id) VALUES (?, ?)');
121
 
122
- for (let i = 0; i < moviesData.length; i++) {
123
- const movie = moviesData[i];
124
- if (!movie.id) continue;
125
-
126
- await movieStmt.run(movie.id, movie.year, movie.type, movie.rating?.kp, movie.rating?.imdb, movie.ageRating, movie.movieLength, movie.isSeries, JSON.stringify(movie));
127
 
128
- if (movie.genres) {
129
- for (const genre of movie.genres) {
130
- const genreId = genreCache.get(genre.name);
131
- if (genreId) await movieGenreStmt.run(movie.id, genreId);
 
 
 
132
  }
133
- }
134
- if (movie.countries) {
135
- for (const country of movie.countries) {
136
- const countryId = countryCache.get(country.name);
137
- if (countryId) await movieCountryStmt.run(movie.id, countryId);
138
  }
139
- }
140
 
141
- if ((i + 1) % 10000 === 0) {
142
- console.log(`Вставлено ${i + 1} / ${moviesData.length} фильмов...`);
 
 
143
  }
144
  }
145
 
@@ -148,28 +160,31 @@ export async function refreshData() {
148
  await movieCountryStmt.finalize();
149
 
150
  await db.exec('COMMIT;');
151
- console.log(`Шаг 4 завершен. Перезагрузка данных успешно окончена.`);
152
 
153
  } catch (error) {
154
  console.error('Критическая ошибка во время обновления данных:', error);
155
  try {
156
  await db.exec('ROLLBACK;');
 
157
  } catch (rollbackError) {
158
  console.error('Ошибка при откате транзакции:', rollbackError);
159
  }
160
  }
161
  }
162
 
163
-
164
  // --- Функции для API остаются без изменений ---
 
165
  export async function getMovieById(id) {
166
  const row = await db.get('SELECT data FROM movies WHERE id = ?', id);
167
  return row ? JSON.parse(row.data) : null;
168
  }
 
169
  export async function getRandomMovie() {
170
  const row = await db.get('SELECT data FROM movies ORDER BY RANDOM() LIMIT 1');
171
  return row ? JSON.parse(row.data) : null;
172
  }
 
173
  export async function getMovies(options) {
174
  // Эта функция остается без изменений, так как она уже была достаточно оптимизирована для чтения
175
  const { filters, pagination, sorting } = options;
 
17
  console.log('Подключение к SQLite (Main API) установлено.');
18
 
19
  await db.exec('PRAGMA journal_mode = WAL;');
20
+ await db.exec('PRAGMA synchronous = NORMAL;');
21
 
 
22
  await db.exec(`
23
  CREATE TABLE IF NOT EXISTS movies (
24
  id INTEGER PRIMARY KEY,
 
51
  }
52
 
53
  /**
54
+ * Переработанная функция загрузки данных, использующая потоковую обработку
55
+ * для предотвращения переполнения памяти.
56
  */
57
  export async function refreshData() {
58
  console.log('Начинается высокопроизводительная перезагрузка данных...');
59
  try {
60
+ // --- ШАГ 1: Сканирование файла для сбора метаданных (жанры, страны) ---
61
+ console.log('Шаг 1: Сканирование файла для сбора жанров и стран...');
62
+ const response1 = await fetch(DATA_URL);
63
+ if (!response1.ok) throw new Error(`Ошибка загрузки на первом проходе: ${response1.statusText}`);
64
+
65
+ const gunzip1 = createGunzip();
66
+ response1.body.pipe(gunzip1);
67
+ const rl1 = createInterface({ input: gunzip1, crlfDelay: Infinity });
68
+
 
69
  const allGenres = new Set();
70
  const allCountries = new Set();
71
+
72
+ for await (const line of rl1) {
73
  if (line.trim()) {
74
+ // Мы не храним весь объект, только извлекаем нужные данные
75
  const movie = JSON.parse(line);
 
76
  movie.genres?.forEach(g => allGenres.add(g.name));
77
  movie.countries?.forEach(c => allCountries.add(c.name));
78
  }
79
  }
80
+ console.log(`Шаг 1 завершен. Найдено ${allGenres.size} уникальных жанров, ${allCountries.size} уникальных стран.`);
81
 
82
+ // --- Начало транзакции для массовой вставки ---
83
  await db.exec('BEGIN TRANSACTION;');
84
+
85
+ // Очищаем таблицы перед новой загрузкой
86
  await db.exec('DELETE FROM movie_genres;');
87
  await db.exec('DELETE FROM movie_countries;');
88
  await db.exec('DELETE FROM genres;');
 
104
  await countryStmt.finalize();
105
  console.log('Шаг 2 завершен.');
106
 
107
+ // --- ШАГ 3: Кэширование ID жанров и стран для быстрой связи ---
108
  console.log('Шаг 3: Кэширование ID в память...');
109
  const genreCache = new Map();
110
  const countryCache = new Map();
 
114
  countriesFromDb.forEach(c => countryCache.set(c.name, c.id));
115
  console.log('Шаг 3 завершен.');
116
 
117
+ // --- ШАГ 4: Потоковая вставка фильмов и их связей (второй проход) ---
118
+ console.log('Шаг 4: Потоковая запись фильмов и их связей...');
119
+ const response2 = await fetch(DATA_URL);
120
+ if (!response2.ok) throw new Error(`Ошибка загрузки на втором проходе: ${response2.statusText}`);
121
+
122
+ const gunzip2 = createGunzip();
123
+ response2.body.pipe(gunzip2);
124
+ const rl2 = createInterface({ input: gunzip2, crlfDelay: Infinity });
125
+
126
  const movieStmt = await db.prepare('INSERT INTO movies (id, year, type, rating_kp, rating_imdb, age_rating, movie_length, is_series, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
 
127
  const movieGenreStmt = await db.prepare('INSERT OR IGNORE INTO movie_genres (movie_id, genre_id) VALUES (?, ?)');
128
  const movieCountryStmt = await db.prepare('INSERT OR IGNORE INTO movie_countries (movie_id, country_id) VALUES (?, ?)');
129
 
130
+ let processedCount = 0;
131
+ for await (const line of rl2) {
132
+ if (line.trim()) {
133
+ const movie = JSON.parse(line);
134
+ if (!movie.id) continue;
135
 
136
+ await movieStmt.run(movie.id, movie.year, movie.type, movie.rating?.kp, movie.rating?.imdb, movie.ageRating, movie.movieLength, movie.isSeries, JSON.stringify(movie));
137
+
138
+ if (movie.genres) {
139
+ for (const genre of movie.genres) {
140
+ const genreId = genreCache.get(genre.name);
141
+ if (genreId) await movieGenreStmt.run(movie.id, genreId);
142
+ }
143
  }
144
+ if (movie.countries) {
145
+ for (const country of movie.countries) {
146
+ const countryId = countryCache.get(country.name);
147
+ if (countryId) await movieCountryStmt.run(movie.id, countryId);
148
+ }
149
  }
 
150
 
151
+ processedCount++;
152
+ if (processedCount % 10000 === 0) {
153
+ console.log(`Обработано ${processedCount} фильмов...`);
154
+ }
155
  }
156
  }
157
 
 
160
  await movieCountryStmt.finalize();
161
 
162
  await db.exec('COMMIT;');
163
+ console.log(`Шаг 4 завершен. Перезагрузка данных успешно окончена. Всего обработано ${processedCount} фильмов.`);
164
 
165
  } catch (error) {
166
  console.error('Критическая ошибка во время обновления данных:', error);
167
  try {
168
  await db.exec('ROLLBACK;');
169
+ console.log('Транзакция была отменена.');
170
  } catch (rollbackError) {
171
  console.error('Ошибка при откате транзакции:', rollbackError);
172
  }
173
  }
174
  }
175
 
 
176
  // --- Функции для API остаются без изменений ---
177
+
178
  export async function getMovieById(id) {
179
  const row = await db.get('SELECT data FROM movies WHERE id = ?', id);
180
  return row ? JSON.parse(row.data) : null;
181
  }
182
+
183
  export async function getRandomMovie() {
184
  const row = await db.get('SELECT data FROM movies ORDER BY RANDOM() LIMIT 1');
185
  return row ? JSON.parse(row.data) : null;
186
  }
187
+
188
  export async function getMovies(options) {
189
  // Эта функция остается без изменений, так как она уже была достаточно оптимизирована для чтения
190
  const { filters, pagination, sorting } = options;