From 0e2ef4133e9b1a7a2171873dd9e18b750dea6b97 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Mon, 10 Oct 2022 17:02:58 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B0=D0=B4=20=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=BE=D0=BC=20?= =?UTF-8?q?=D0=BA=D0=BD=D0=B8=D0=B3=20=D0=B2=20=D1=81=D0=B5=D1=80=D0=B8?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Api/Api.vue | 10 ++++ client/components/Search/Search.vue | 23 ++++++++ server/controllers/WebSocketController.js | 11 ++++ server/core/DbCreator.js | 28 ++++++--- server/core/DbSearcher.js | 71 ++++++++++++++++++++++- server/core/WebWorker.js | 6 ++ 6 files changed, 139 insertions(+), 10 deletions(-) diff --git a/client/components/Api/Api.vue b/client/components/Api/Api.vue index 3efa035..ed80784 100644 --- a/client/components/Api/Api.vue +++ b/client/components/Api/Api.vue @@ -220,6 +220,16 @@ class Api { return response; } + async getSeriesBookList(seriesId) { + const response = await this.request({action: 'get-series-book-list', seriesId}); + + if (response.error) { + throw new Error(response.error); + } + + return response; + } + async getGenreTree() { const response = await this.request({action: 'get-genre-tree'}); diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue index 20214f2..c9676d6 100644 --- a/client/components/Search/Search.vue +++ b/client/components/Search/Search.vue @@ -878,6 +878,29 @@ class Search { } } + async loadSeriesBooks(seriesId) { + try { + let result; + + if (this.abCacheEnabled) { + const key = `series-${seriesId}-${this.inpxHash}`; + const data = await authorBooksStorage.getData(key); + if (data) { + result = JSON.parse(data); + } else { + result = await this.api.getBookList(seriesId); + await authorBooksStorage.setData(key, JSON.stringify(result)); + } + } else { + result = await this.api.getBookList(seriesId); + } + + return JSON.parse(result.books); + } catch (e) { + this.$root.stdDialog.alert(e.message, 'Ошибка'); + } + } + filterBooks(loadedBooks) { const s = this.search; diff --git a/server/controllers/WebSocketController.js b/server/controllers/WebSocketController.js index e42879e..3adf6f3 100644 --- a/server/controllers/WebSocketController.js +++ b/server/controllers/WebSocketController.js @@ -79,6 +79,8 @@ class WebSocketController { await this.search(req, ws); break; case 'get-book-list': await this.getBookList(req, ws); break; + case 'get-series-book-list': + await this.getSeriesBookList(req, ws); break; case 'get-genre-tree': await this.getGenreTree(req, ws); break; case 'get-book-link': @@ -150,6 +152,15 @@ class WebSocketController { this.send(result, req, ws); } + async getSeriesBookList(req, ws) { + if (!utils.hasProp(req, 'seriesId')) + throw new Error(`seriesId is empty`); + + const result = await this.webWorker.getSeriesBookList(req.seriesId); + + this.send(result, req, ws); + } + async getGenreTree(req, ws) { const result = await this.webWorker.getGenreTree(); diff --git a/server/core/DbCreator.js b/server/core/DbCreator.js index 8323721..0a432ec 100644 --- a/server/core/DbCreator.js +++ b/server/core/DbCreator.js @@ -307,12 +307,12 @@ class DbCreator { callback({progress: 1}); //чистка памяти, ибо жрет как не в себя - await db.drop({table: 'book'}); + await db.close({table: 'book'}); await db.freeMemory(); utils.freeMemory(); //парсинг 2, подготовка - const parseField = (fieldValue, fieldMap, fieldArr, authorIds) => { + const parseField = (fieldValue, fieldMap, fieldArr, authorIds, bookId) => { if (!fieldValue) fieldValue = emptyFieldValue; @@ -324,6 +324,8 @@ class DbCreator { fieldRec = fieldArr[fieldId]; } else { fieldRec = {id: fieldArr.length, value, authorId: new Set()}; + if (bookId) + fieldRec.bookId = new Set(); fieldArr.push(fieldRec); fieldMap.set(value, fieldRec.id); } @@ -331,6 +333,9 @@ class DbCreator { for (const id of authorIds) { fieldRec.authorId.add(id); } + + if (bookId) + fieldRec.bookId.add(bookId); }; const parseBookRec = (rec) => { @@ -346,7 +351,7 @@ class DbCreator { } //серии - parseField(rec.series, seriesMap, seriesArr, authorIds); + parseField(rec.series, seriesMap, seriesArr, authorIds, rec.id); //названия parseField(rec.title, titleMap, titleArr, authorIds); @@ -464,7 +469,7 @@ class DbCreator { //сохраним поисковые таблицы const chunkSize = 10000; - const saveTable = async(table, arr, nullArr, authorIdToArray = true) => { + const saveTable = async(table, arr, nullArr, authorIdToArray = false, bookIdToArray = false) => { arr.sort((a, b) => a.value.localeCompare(b.value)); @@ -482,6 +487,11 @@ class DbCreator { rec.authorId = Array.from(rec.authorId); } + if (bookIdToArray) { + for (const rec of chunk) + rec.bookId = Array.from(rec.bookId); + } + await db.insert({table, rows: chunk}); if (i % 5 == 0) { @@ -500,23 +510,23 @@ class DbCreator { //author callback({job: 'author save', jobMessage: 'Сохранение индекса авторов', jobStep: 6, progress: 0}); - await saveTable('author', authorArr, () => {authorArr = null}, false); + await saveTable('author', authorArr, () => {authorArr = null}); //series callback({job: 'series save', jobMessage: 'Сохранение индекса серий', jobStep: 7, progress: 0}); - await saveTable('series', seriesArr, () => {seriesArr = null}); + await saveTable('series', seriesArr, () => {seriesArr = null}, true, true); //title callback({job: 'title save', jobMessage: 'Сохранение индекса названий', jobStep: 8, progress: 0}); - await saveTable('title', titleArr, () => {titleArr = null}); + await saveTable('title', titleArr, () => {titleArr = null}, true); //genre callback({job: 'genre save', jobMessage: 'Сохранение индекса жанров', jobStep: 9, progress: 0}); - await saveTable('genre', genreArr, () => {genreArr = null}); + await saveTable('genre', genreArr, () => {genreArr = null}, true); //lang callback({job: 'lang save', jobMessage: 'Сохранение индекса языков', jobStep: 10, progress: 0}); - await saveTable('lang', langArr, () => {langArr = null}); + await saveTable('lang', langArr, () => {langArr = null}, true); //кэш-таблицы запросов await db.create({table: 'query_cache'}); diff --git a/server/core/DbSearcher.js b/server/core/DbSearcher.js index 39eb302..c903f28 100644 --- a/server/core/DbSearcher.js +++ b/server/core/DbSearcher.js @@ -273,7 +273,7 @@ class DbSearcher { }); let author = ''; - let books = []; + let books = ''; if (rows.length) { author = rows[0].author; @@ -331,6 +331,75 @@ class DbSearcher { } } + async selectSeriesBookList(seriesId) { + const db = this.db; + + //выборка серии по seriesId + const rows = await db.select({ + table: 'series', + where: `@@id(${db.esc(seriesId)})` + }); + + let books = []; + + if (rows.length) { + books = await db.select({ + table: 'book', + where: `@@id(${db.esc(rows[0].bookId)})` + }); + } + + return {books: JSON.stringify(books)}; + } + + async getSeriesBookList(seriesId) { + if (this.closed) + throw new Error('DbSearcher closed'); + + this.searchFlag++; + + try { + const db = this.db; + + let result; + + if (this.config.queryCacheEnabled) { + const key = `series_books-${seriesId}`; + const rows = await db.select({table: 'query_cache', where: `@@id(${db.esc(key)})`}); + + if (rows.length) {//нашли в кеше + await db.insert({ + table: 'query_time', + replace: true, + rows: [{id: key, time: Date.now()}], + }); + + result = rows[0].value; + } else {//не нашли в кеше + result = await this.selectSeriesBookList(seriesId); + + //кладем в кеш + await db.insert({ + table: 'query_cache', + replace: true, + rows: [{id: key, value: result}], + }); + await db.insert({ + table: 'query_time', + replace: true, + rows: [{id: key, time: Date.now()}], + }); + } + } else { + result = await this.selectSeriesBookList(seriesId); + } + + return result; + } finally { + this.searchFlag--; + } + } + async periodicCleanCache() { this.timer = null; const cleanInterval = this.config.cacheCleanInterval*60*1000; diff --git a/server/core/WebWorker.js b/server/core/WebWorker.js index 387edfb..b877a32 100644 --- a/server/core/WebWorker.js +++ b/server/core/WebWorker.js @@ -227,6 +227,12 @@ class WebWorker { return await this.dbSearcher.getBookList(authorId); } + async getSeriesBookList(seriesId) { + this.checkMyState(); + + return await this.dbSearcher.getSeriesBookList(seriesId); + } + async getGenreTree() { this.checkMyState();