浏览代码

Работа над поиском по дате поступления и оценке

Book Pauk 2 年之前
父节点
当前提交
fee3231d10
共有 4 个文件被更改,包括 161 次插入6 次删除
  1. 16 0
      client/components/Search/BaseList.js
  2. 37 4
      client/components/Search/Search.vue
  3. 19 1
      server/core/DbCreator.js
  4. 89 1
      server/core/DbSearcher.js

+ 16 - 0
client/components/Search/BaseList.js

@@ -402,12 +402,28 @@ export default class BaseList {
                 langFound = searchLang.has(book.lang || emptyFieldValue);
             }
 
+            //date
+            let dateFound = !s.date;
+            if (!dateFound) {
+                let [from = '0000-00-00', to = '9999-99-99'] = s.date.split(',');
+                dateFound = (book.date >= from && book.date <= to);
+            }
+
+            //librate
+            let librateFound = !s.librate;
+            if (!librateFound) {
+                const searchLibrate = new Set(s.librate.split(',').map(n => parseInt(n, 10)).filter(n => !isNaN(n)));
+                librateFound = searchLibrate.has(book.librate);
+            }
+
             return (this.showDeleted || !book.del)
                 && authorFound
                 && filterBySearch(book.series, s.series)
                 && filterBySearch(book.title, s.title)
                 && genreFound
                 && langFound
+                && dateFound
+                && librateFound
             ;
         });
     }

+ 37 - 4
client/components/Search/Search.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="root column fit" style="position: relative">
         <div ref="scroller" class="col fit column no-wrap" style="overflow: auto; position: relative" @scroll="onScroll">
-            <div ref="toolPanel" class="tool-panel column bg-cyan-2" style="position: sticky; top: 0; z-index: 10;">
+            <div ref="toolPanel" class="tool-panel q-pb-xs column bg-cyan-2" style="position: sticky; top: 0; z-index: 10;">
                 <div class="header q-mx-md q-mb-xs q-mt-sm row items-center">
                     <a :href="newSearchLink" style="height: 33px">
                         <img src="./assets/logo.png" />
@@ -46,7 +46,7 @@
                         {{ projectName }}
                     </div>
                 </div>
-                <div class="row q-mx-md q-mb-sm items-center">
+                <div class="row q-mx-md q-mb-xs items-center">
                     <q-input
                         ref="authorInput" v-model="search.author" :maxlength="5000" :debounce="inputDebounce"
                         class="q-mt-xs" :bg-color="inputBgColor('author')" style="width: 200px;" label="Автор" stack-label outlined dense clearable
@@ -79,6 +79,10 @@
                         class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 80px;" label="Язык" stack-label outlined dense clearable readonly
                         @click="selectLang"
                     >
+                        <template v-if="search.lang" #append>
+                            <q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.lang = ''" />
+                        </template>
+
                         <q-tooltip v-if="search.lang && showTooltips" :delay="500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
                             {{ search.lang }}
                         </q-tooltip>
@@ -95,10 +99,10 @@
                         </q-tooltip>
                     </DivBtn>
                 </div>
-                <div v-show="extendedParams" class="row q-mx-md q-mb-sm items-center">
+                <div v-show="extendedParams" class="row q-mx-md q-mb-xs items-center">
                     <q-input
                         v-model="genreNames" :maxlength="inputMaxLength" :debounce="inputDebounce"
-                        :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 200px;" label="Жанр" stack-label outlined dense clearable readonly
+                        class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 200px;" label="Жанр" stack-label outlined dense clearable readonly
                         @click="selectGenre"
                     >
                         <template v-if="genreNames" #append>
@@ -109,6 +113,30 @@
                             {{ genreNames }}
                         </q-tooltip>
                     </q-input>
+
+                    <div class="q-mx-xs" />
+                    <q-input
+                        v-model="search.date" :maxlength="inputMaxLength" :debounce="inputDebounce"
+                        class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 200px;" label="Дата поступления" stack-label outlined dense clearable
+                    >
+                        <q-tooltip v-if="search.date && showTooltips" :delay="500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
+                            {{ search.date }}
+                        </q-tooltip>
+                    </q-input>
+
+                    <div class="q-mx-xs" />
+                    <q-input
+                        v-model="search.librate" :maxlength="inputMaxLength" :debounce="inputDebounce"
+                        class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 180px;" label="Оценка" stack-label outlined dense clearable
+                    >
+                        <template v-if="search.librate" #append>
+                            <q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.librate = ''" />
+                        </template>
+
+                        <q-tooltip v-if="search.librate && showTooltips" :delay="500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
+                            {{ search.librate }}
+                        </q-tooltip>
+                    </q-input>
                 </div>
                 <div v-show="!extendedParams && extendedParamsMessage" class="row q-mx-md q-mb-sm items-center clickable" @click="extendedParams = true">
                     +{{ extendedParamsMessage }}
@@ -325,6 +353,8 @@ class Search {
                 title: search.title || '',
                 genre: search.genre || '',
                 lang: search.lang || '',
+                date: search.date || '',
+                librate: search.librate || '',
                 page: search.page || 1,
                 limit: search.limit || 50,
             });
@@ -776,6 +806,8 @@ class Search {
                 title: query.title,
                 genre: query.genre,
                 lang: (typeof(query.lang) == 'string' ? query.lang : this.langDefault),
+                date: query.date,
+                librate: query.librate,
                 page: parseInt(query.page, 10),
                 limit: parseInt(query.limit, 10) || this.search.limit,
             })
@@ -793,6 +825,7 @@ class Search {
         try {
             const oldQuery = this.$route.query;
             const cloned = _.cloneDeep(this.search);
+
             delete cloned.setDefaults;
 
             const query = _.pickBy(cloned);

+ 19 - 1
server/core/DbCreator.js

@@ -60,6 +60,10 @@ class DbCreator {
         let langArr = [];
         let delMap = new Map();//удаленные
         let delArr = [];
+        let dateMap = new Map();//дата поступления
+        let dateArr = [];
+        let librateMap = new Map();//оценка
+        let librateArr = [];
 
         //stats
         let authorCount = 0;
@@ -393,6 +397,12 @@ class DbCreator {
             
             //удаленные
             parseField(rec.del, delMap, delArr, authorIds);
+
+            //дата поступления
+            parseField(rec.date, dateMap, dateArr, authorIds);
+
+            //оценка
+            parseField(rec.librate, librateMap, librateArr, authorIds);
         };
 
         callback({job: 'search tables create', jobMessage: 'Создание поисковых таблиц', jobStep: 4, progress: 0});
@@ -448,6 +458,8 @@ class DbCreator {
         genreMap = null;
         langMap = null;
         delMap = null;
+        dateMap = null;
+        librateMap = null;
 
         utils.freeMemory();
 
@@ -553,13 +565,19 @@ class DbCreator {
         callback({job: 'genre save', jobMessage: 'Сохранение индекса жанров', jobStep: 9, progress: 0});
         await saveTable('genre', genreArr, () => {genreArr = null}, true);
 
+        callback({job: 'others save', jobMessage: 'Сохранение остальных индексов', jobStep: 10, progress: 0});
         //lang
-        callback({job: 'lang save', jobMessage: 'Сохранение индекса языков', jobStep: 10, progress: 0});
         await saveTable('lang', langArr, () => {langArr = null}, true);
 
         //del
         await saveTable('del', delArr, () => {delArr = null}, true, false, 'number');
 
+        //date
+        await saveTable('date', dateArr, () => {dateArr = null}, true);
+
+        //librate
+        await saveTable('librate', librateArr, () => {librateArr = null}, true, false, 'number');
+
         //кэш-таблицы запросов
         await db.create({table: 'query_cache'});
         await db.create({table: 'query_time'});

+ 89 - 1
server/core/DbSearcher.js

@@ -25,7 +25,7 @@ class DbSearcher {
     }
 
     queryKey(q) {
-        return JSON.stringify([q.author, q.series, q.title, q.genre, q.lang, q.del]);
+        return JSON.stringify([q.author, q.series, q.title, q.genre, q.lang, q.del, q.date, q.librate]);
     }
 
     getWhere(a) {
@@ -259,6 +259,74 @@ class DbSearcher {
 
             idsArr.push(delIds);
         }
+
+        //дата поступления
+        if (query.date) {
+            const dateKey = `author-ids-date-${query.date}`;
+            let dateIds = await this.getCached(dateKey);
+
+            if (dateIds === null) {
+                let [from = '', to = ''] = query.date.split(',');
+
+                const dateRows = await db.select({
+                    table: 'date',
+                    rawResult: true,
+                    where: `
+                        const ids = @indexLR('value', ${db.esc(from)} || undefined, ${db.esc(to)} || undefined);
+                        
+                        const result = new Set();
+                        for (const id of ids) {
+                            const row = @unsafeRow(id);
+                            for (const authorId of row.authorId)
+                                result.add(authorId);
+                        }
+
+                        return Array.from(result);
+                    `
+                });
+
+                dateIds = dateRows[0].rawResult;
+                await this.putCached(dateKey, dateIds);
+            }
+
+            idsArr.push(dateIds);
+        }
+
+        //оценка
+        if (query.librate) {
+            const librateKey = `author-ids-librate-${query.librate}`;
+            let librateIds = await this.getCached(librateKey);
+
+            if (librateIds === null) {
+                const dateRows = await db.select({
+                    table: 'librate',
+                    rawResult: true,
+                    where: `
+                        const rates = ${db.esc(query.librate.split(',').map(n => parseInt(n, 10)).filter(n => !isNaN(n)))};
+
+                        const ids = new Set();
+                        for (const rate of rates) {
+                            for (const id of @indexLR('value', rate, rate))
+                                ids.add(id);
+                        }
+                        
+                        const result = new Set();
+                        for (const id of ids) {
+                            const row = @unsafeRow(id);
+                            for (const authorId of row.authorId)
+                                result.add(authorId);
+                        }
+
+                        return Array.from(result);
+                    `
+                });
+
+                librateIds = dateRows[0].rawResult;
+                await this.putCached(librateKey, librateIds);
+            }
+
+            idsArr.push(librateIds);
+        }
 /*
         //ищем пересечение множеств
         idsArr.push(authorIds);
@@ -341,6 +409,26 @@ class DbSearcher {
             `;            
         }
 
+        //дата поступления
+        if (query.date) {
+            let [from = '0000-00-00', to = '9999-99-99'] = query.date.split(',');
+            filter += `
+                if (!(book.date >= ${db.esc(from)} && book.date <= ${db.esc(to)}))
+                    return false;
+            `;
+        }
+
+        //оценка
+        if (query.librate) {
+            closures += `
+                const searchLibrate = new Set(${db.esc(query.librate.split(',').map(n => parseInt(n, 10)).filter(n => !isNaN(n)))});
+            `;
+            filter += `
+                if (!searchLibrate.has(book.librate))
+                    return false;
+            `;
+        }
+
         //серии
         if (exclude !== 'series' && query.series && query.series !== '*') {
             closures += `