diff --git a/client/components/Api/Api.vue b/client/components/Api/Api.vue index da03f43..bdb90c0 100644 --- a/client/components/Api/Api.vue +++ b/client/components/Api/Api.vue @@ -24,6 +24,20 @@ class Api { } mounted() { + this.updateConfig();//no await + } + + async updateConfig() { + try { + const config = await this.getConfig(); + this.commit('setConfig', config); + } catch (e) { + this.$root.stdDialog.alert(e.message, 'Ошибка'); + } + } + + get config() { + return this.$store.state.config; } async request(params) { @@ -40,7 +54,7 @@ class Api { return response; } - async config() { + async getConfig() { const response = await this.request({action: 'get-config'}); if (response.error) { diff --git a/client/components/App.vue b/client/components/App.vue index 86414e4..b163c00 100644 --- a/client/components/App.vue +++ b/client/components/App.vue @@ -91,17 +91,6 @@ class App { this.$root.stdDialog = this.$refs.stdDialog; this.setAppTitle(); - - (async() => { - try { - const api = this.$root.api; - const config = await api.config(); - this.commit('setConfig', config); - } catch (e) { - this.$root.stdDialog.alert(e.message, 'Ошибка'); - } - })(); - } get config() { diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue index eef4f39..2e2c1b3 100644 --- a/client/components/Search/Search.vue +++ b/client/components/Search/Search.vue @@ -1,7 +1,7 @@ @@ -55,12 +88,32 @@ class Search { collection = ''; projectName = ''; + //input field consts + inputMaxLength = 1000; + inputDebounce = 400; + //search fields author = ''; series = ''; title = ''; genre = ''; lang = ''; + limit = 100; + + //stuff + queryFound = 0; + totalFound = 0; + + limitOptions = [ + {label: '10', value: 10}, + {label: '20', value: 20}, + {label: '50', value: 50}, + {label: '100', value: 100}, + {label: '1000', value: 1000}, + ]; + + searchResult = {}; + tableData = []; created() { this.commit = this.$store.commit; @@ -101,7 +154,23 @@ class Search { `, 'Статистика по коллекции', {iconName: 'la la-info-circle'}); } + selectGenre() { + this.$root.stdDialog.alert('Выбор жанра'); + } + + selectLang() { + this.$root.stdDialog.alert('Выбор языка'); + } + async updateTableData() { + let result = []; + + let id = 0; + for (const rec of this.searchResult.author) { + result.push({key: id++, value: rec.author}); + } + + this.tableData = result; } async refresh() { @@ -111,6 +180,7 @@ class Search { title: this.title, genre: this.genre, lang: this.lang, + limit: this.limit, }; this.queryExecute = newQuery; @@ -125,13 +195,17 @@ class Search { this.queryExecute = null; try { - this.searchResult = await this.api.search(query); + const result = await this.api.search(query); + + this.queryFound = result.author.length; + this.totalFound = result.totalFound; + + this.searchResult = result; + this.updateTableData();//no await } catch (e) { this.$root.stdDialog.alert(e.message, 'Ошибка'); return; } - - this.updateTableData();//no await } } finally { this.refreshing = false; @@ -153,7 +227,7 @@ export default vueComponent(Search); .header { font-size: 150%; - height: 30px; + min-height: 30px; } .clickable { diff --git a/client/quasar.js b/client/quasar.js index 0702203..c02da38 100644 --- a/client/quasar.js +++ b/client/quasar.js @@ -25,7 +25,7 @@ import {QIcon} from 'quasar/src/components/icon'; //import {QSpinner} from 'quasar/src/components/spinner'; //import {QTable, QTh, QTr, QTd} from 'quasar/src/components/table'; //import {QCheckbox} from 'quasar/src/components/checkbox'; -//import {QSelect} from 'quasar/src/components/select'; +import {QSelect} from 'quasar/src/components/select'; //import {QColor} from 'quasar/src/components/color'; //import {QPopupProxy} from 'quasar/src/components/popup-proxy'; import {QDialog} from 'quasar/src/components/dialog'; @@ -56,7 +56,7 @@ const components = { //QSpinner, //QTable, QTh, QTr, QTd, //QCheckbox, - //QSelect, + QSelect, //QColor, //QPopupProxy, QDialog, diff --git a/server/controllers/WebSocketController.js b/server/controllers/WebSocketController.js index 4ec35ac..3078920 100644 --- a/server/controllers/WebSocketController.js +++ b/server/controllers/WebSocketController.js @@ -66,6 +66,8 @@ class WebSocketController { await this.getConfig(req, ws); break; case 'get-worker-state': await this.getWorkerState(req, ws); break; + case 'search': + await this.search(req, ws); break; default: throw new Error(`Action not found: ${req.action}`); @@ -112,6 +114,14 @@ class WebSocketController { this.send((state ? state : {}), req, ws); } + async search(req, ws) { + if (!req.query) + throw new Error(`query is empty`); + + const result = await this.webWorker.search(req.query); + + this.send(result, req, ws); + } } module.exports = WebSocketController; diff --git a/server/core/DbCreator.js b/server/core/DbCreator.js index 2adb1a9..c7a1db4 100644 --- a/server/core/DbCreator.js +++ b/server/core/DbCreator.js @@ -302,6 +302,9 @@ class DbCreator { await db.close({table: 'lang'}); utils.freeMemory(); + //кэш-таблицы + + callback({job: 'done', jobMessage: ''}); } } diff --git a/server/core/DbSearcher.js b/server/core/DbSearcher.js index 82b4b8e..91d4804 100644 --- a/server/core/DbSearcher.js +++ b/server/core/DbSearcher.js @@ -1,27 +1,82 @@ +//const _ = require('lodash'); + +const utils = require('./utils'); + class DbSearcher { constructor(db) { this.db = db; } - async search(query) { + async selectAuthorIds(query) { const db = this.db; - let result = []; - + let authorRows; + //сначала выберем все id авторов по фильтру + //порядок id соответсвует ASC-сортировке по author if (query.author) { // } else { - result = await db.select({ + authorRows = await db.select({ table: 'author', - map: `(r) => ({id: r.id, author: r.author})` + map: `(r) => ({id: r.id})`, }); } - if (query.limit) { - result = result.slice(0, query.limit); - } + let authorIds = new Set(); + for (const row of authorRows) + authorIds.add(row.id); - return result; + const idsArr = []; + idsArr.push(authorIds); + + //серии + //названия + //жанры + //языки + + if (idsArr.length > 1) + authorIds = utils.intersectSet(idsArr); + + //сортировка + authorIds = Array.from(authorIds); + authorIds.sort(); + + return authorIds; + } + + async getAuthorIds(query) { + const db = this.db; + + if (!db.searchCache) + db.searchCache = {}; + + /*const q = query; + const key = JSON.stringify([q.author, ]); + query); + delete q.limit; + + q = */ + return await this.selectAuthorIds(query); + } + + async search(query) { + const db = this.db; + + const authorIds = await this.getAuthorIds(query); + + const totalFound = authorIds.length; + const limit = (query.limit ? query.limit : 1000); + + //выборка найденных авторов + let result = await db.select({ + table: 'author', + map: `(r) => ({id: r.id, author: r.author})`, + where: `@@id(${db.esc(authorIds)})` + }); + + result = result.slice(0, limit); + + return {result, totalFound}; } } diff --git a/server/core/WebWorker.js b/server/core/WebWorker.js index c306b72..b0301cc 100644 --- a/server/core/WebWorker.js +++ b/server/core/WebWorker.js @@ -176,6 +176,19 @@ class WebWorker { return db.wwCache.config; } + async search(query) { + this.checkMyState(); + + const config = await this.dbConfig(); + const result = await this.dbSearcher.search(query); + + return { + author: result.result, + totalFound: result.totalFound, + inpxHash: (config.inpxHash ? config.inpxHash : ''), + }; + } + async logServerStats() { while (1) {// eslint-disable-line try { diff --git a/server/core/utils.js b/server/core/utils.js index e100e45..6c7d8f9 100644 --- a/server/core/utils.js +++ b/server/core/utils.js @@ -59,6 +59,38 @@ function getBufHash(buf, hashName, enc) { return hash.digest(enc); } +function intersectSet(arrSet) { + if (!arrSet.length) + return new Set(); + + let min = 0; + let size = arrSet[0].size; + for (let i = 1; i < arrSet.length; i++) { + if (arrSet[i].size < size) { + min = i; + size = arrSet[i].size; + } + } + + const result = new Set(); + for (const elem of arrSet[min]) { + let inAll = true; + for (let i = 0; i < arrSet.length; i++) { + if (i === min) + continue; + if (!arrSet[i].has(elem)) { + inAll = false; + break; + } + } + + if (inAll) + result.add(elem); + } + + return result; +} + module.exports = { sleep, versionText, @@ -68,4 +100,5 @@ module.exports = { freeMemory, getFileHash, getBufHash, + intersectSet, }; \ No newline at end of file