diff --git a/server/config/base.js b/server/config/base.js index d2ea36f..952abaf 100644 --- a/server/config/base.js +++ b/server/config/base.js @@ -17,7 +17,7 @@ module.exports = { //поправить в случае, если были критические изменения в DbCreator //иначе будет рассинхронизация между сервером и клиентом на уровне БД - dbVersion: '3', + dbVersion: '4', dbCacheSize: 5, maxPayloadSize: 500,//in MB diff --git a/server/core/DbCreator.js b/server/core/DbCreator.js index a5823e6..67653d0 100644 --- a/server/core/DbCreator.js +++ b/server/core/DbCreator.js @@ -357,7 +357,7 @@ class DbCreator { parseField(rec.series, seriesMap, seriesArr, authorIds, rec.id); //названия - parseField(rec.title, titleMap, titleArr, authorIds); + parseField(rec.title, titleMap, titleArr, authorIds, rec.id); //жанры let genre = rec.genre || emptyFieldValue; @@ -533,7 +533,7 @@ class DbCreator { //title callback({job: 'title save', jobMessage: 'Сохранение индекса названий', jobStep: 8, progress: 0}); - await saveTable('title', titleArr, () => {titleArr = null}, true); + await saveTable('title', titleArr, () => {titleArr = null}, true, true); //genre callback({job: 'genre save', jobMessage: 'Сохранение индекса жанров', jobStep: 9, progress: 0}); @@ -557,7 +557,16 @@ class DbCreator { }); callback({job: 'optimization', jobMessage: 'Оптимизация', jobStep: 11, progress: 0}); - await this.optimizeSeries(db, callback); + await this.optimizeSeries(db, (p) => { + if (p.progress) + p.progress = 0.5*p.progress; + callback(p); + }); + await this.optimizeTitle(db, (p) => { + if (p.progress) + p.progress = 0.5*(1 + p.progress); + callback(p); + }); callback({job: 'stats count', jobMessage: 'Подсчет статистики', jobStep: 12, progress: 0}); await this.countStats(db, callback, stats); @@ -672,6 +681,95 @@ class DbCreator { await db.close({table: 'series'}); } + async optimizeTitle(db, callback) { + //оптимизация title, превращаем массив bookId в books, кладем все в title_book + await db.open({table: 'title'}); + + await db.create({ + table: 'title_book', + flag: {name: 'toDel', check: 'r => r.toDel'}, + }); + + const saveTitleChunk = async(titleChunk) => { + const ids = []; + for (const s of titleChunk) { + for (const id of s.bookId) { + ids.push(id); + } + } + + ids.sort((a, b) => a - b);// обязательно, иначе будет тормозить - особенности JembaDb + + const rows = await db.select({table: 'book', where: `@@id(${db.esc(ids)})`}); + + const bookArr = new Map(); + for (const row of rows) + bookArr.set(row.id, row); + + for (const s of titleChunk) { + s.books = []; + s.bookCount = 0; + s.bookDelCount = 0; + for (const id of s.bookId) { + const rec = bookArr.get(id); + if (rec) {//на всякий случай + s.books.push(rec); + if (!rec.del) + s.bookCount++; + else + s.bookDelCount++; + } + } + + if (s.books.length) { + s.series = s.books[0].series; + } else { + s.toDel = 1; + } + + delete s.authorId; + delete s.bookId; + } + + await db.insert({ + table: 'title_book', + rows: titleChunk, + }); + }; + + const rows = await db.select({table: 'title'}); + + let idsLen = 0; + let chunk = []; + let processed = 0; + for (const row of rows) {// eslint-disable-line + chunk.push(row); + idsLen += row.bookId.length; + processed++; + + if (idsLen > 20000) {//константа выяснена эмпирическим путем "память/скорость" + await saveTitleChunk(chunk); + + idsLen = 0; + chunk = []; + + callback({progress: processed/rows.length}); + + await utils.sleep(100); + utils.freeMemory(); + await db.freeMemory(); + } + } + if (chunk.length) { + await saveTitleChunk(chunk); + chunk = null; + } + + await db.delete({table: 'title_book', where: `@@flag('toDel')`}); + await db.close({table: 'title_book'}); + await db.close({table: 'title'}); + } + async countStats(db, callback, stats) { //статистика по количеству файлов