Просмотр исходного кода

transplants methods for search from crowi

Yuki Takei 7 лет назад
Родитель
Сommit
21cab688df
4 измененных файлов с 128 добавлено и 98 удалено
  1. 4 0
      src/server/models/bookmark.js
  2. 9 10
      src/server/models/page.js
  3. 75 63
      src/server/routes/admin.js
  4. 40 25
      src/server/routes/search.js

+ 4 - 0
src/server/models/bookmark.js

@@ -13,6 +13,10 @@ module.exports = function(crowi) {
   });
   bookmarkSchema.index({page: 1, user: 1}, {unique: true});
 
+  bookmarkSchema.statics.countByPageId = async function(pageId) {
+    return await this.count({ page: pageId });
+  };
+
   bookmarkSchema.statics.populatePage = async function(bookmarks) {
     const Bookmark = this;
     const User = crowi.model('User');

+ 9 - 10
src/server/models/page.js

@@ -845,22 +845,17 @@ module.exports = function(crowi) {
    * Bulk get (for internal only)
    */
   pageSchema.statics.getStreamOfFindAll = function(options) {
-    var Page = this
-      , options = options || {}
-      , publicOnly = options.publicOnly || true
-      , criteria = {redirectTo: null, }
-      ;
+    const opt = options || {};
+    const publicOnly = opt.publicOnly || true;
+    const criteria = { redirectTo: null };
 
     if (publicOnly) {
       criteria.grant = GRANT_PUBLIC;
     }
 
     return this.find(criteria)
-      .populate([
-        {path: 'creator', model: 'User'},
-        {path: 'revision', model: 'Revision'},
-      ])
-      .sort({updatedAt: -1})
+      .populate([{ path: 'creator', model: 'User' }, { path: 'revision', model: 'Revision' }])
+      .lean()
       .cursor();
   };
 
@@ -1263,6 +1258,10 @@ module.exports = function(crowi) {
     return addSlashOfEnd(path);
   };
 
+  pageSchema.statics.allPageCount = function() {
+    return this.count({ redirectTo: null });
+  };
+
   pageSchema.statics.GRANT_PUBLIC = GRANT_PUBLIC;
   pageSchema.statics.GRANT_RESTRICTED = GRANT_RESTRICTED;
   pageSchema.statics.GRANT_SPECIFIED = GRANT_SPECIFIED;

+ 75 - 63
src/server/routes/admin.js

@@ -1,28 +1,33 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  const debug = require('debug')('growi:routes:admin')
-    , logger = require('@alias/logger')('growi:routes:admin')
-    , fs = require('fs')
-    , models = crowi.models
-    , Page = models.Page
-    , PageGroupRelation = models.PageGroupRelation
-    , User = models.User
-    , ExternalAccount = models.ExternalAccount
-    , UserGroup = models.UserGroup
-    , UserGroupRelation = models.UserGroupRelation
-    , Config = models.Config
-    , GlobalNotificationSetting = models.GlobalNotificationSetting
-    , GlobalNotificationMailSetting = models.GlobalNotificationMailSetting
-    , GlobalNotificationSlackSetting = models.GlobalNotificationSlackSetting  // eslint-disable-line no-unused-vars
-    , PluginUtils = require('../plugins/plugin-utils')
-    , pluginUtils = new PluginUtils()
-    , ApiResponse = require('../util/apiResponse')
-    , recommendedXssWhiteList = require('@commons/service/xss/recommendedXssWhiteList')
-    , importer = require('../util/importer')(crowi)
-
-    , MAX_PAGE_LIST = 50
-    , actions = {};
+  const debug = require('debug')('growi:routes:admin');
+  const logger = require('@alias/logger')('growi:routes:admin');
+  const fs = require('fs');
+
+  const models = crowi.models;
+  const Page = models.Page;
+  const PageGroupRelation = models.PageGroupRelation;
+  const User = models.User;
+  const ExternalAccount = models.ExternalAccount;
+  const UserGroup = models.UserGroup;
+  const UserGroupRelation = models.UserGroupRelation;
+  const Config = models.Config;
+  const GlobalNotificationSetting = models.GlobalNotificationSetting;
+  const GlobalNotificationMailSetting = models.GlobalNotificationMailSetting;
+  const GlobalNotificationSlackSetting = models.GlobalNotificationSlackSetting; // eslint-disable-line no-unused-vars
+
+  const recommendedXssWhiteList = require('@commons/service/xss/recommendedXssWhiteList');
+  const PluginUtils = require('../plugins/plugin-utils');
+  const ApiResponse = require('../util/apiResponse');
+  const importer = require('../util/importer')(crowi);
+
+  const searchEvent = crowi.event('search');
+  const pluginUtils = new PluginUtils();
+
+  const MAX_PAGE_LIST = 50;
+  const actions = {};
+
 
   function createPager(total, limit, page, pagesCount, maxPageList) {
     const pager = {
@@ -294,12 +299,6 @@ module.exports = function(crowi, app) {
     });
   };
 
-  actions.search = {};
-  actions.search.index = function(req, res) {
-    return res.render('admin/search', {
-    });
-  };
-
   // app.post('/admin/notification/slackIwhSetting' , admin.notification.slackIwhSetting);
   actions.notification.slackIwhSetting = function(req, res) {
     var slackIwhSetting = req.form.slackIwhSetting;
@@ -425,48 +424,61 @@ module.exports = function(crowi, app) {
     return triggerEvents;
   };
 
-  actions.search.buildIndex = function(req, res) {
-    var search = crowi.getSearcher();
+  actions.search = {}
+  actions.search.index = function(req, res) {
+    const search = crowi.getSearcher();
     if (!search) {
       return res.redirect('/admin');
     }
 
-    return new Promise(function(resolve, reject) {
-      search.deleteIndex()
-        .then(function(data) {
-          debug('Index deleted.');
-          resolve();
-        }).catch(function(err) {
-          debug('Delete index Error, but if it is initialize, its ok.', err);
-          resolve();
-        });
-    })
-    .then(function() {
-      return search.buildIndex();
-    })
-    .then(function(data) {
-      if (!data.errors) {
-        debug('Index created.');
-      }
-      return search.addAllPages();
-    })
-    .then(function(data) {
-      if (!data.errors) {
-        debug('Data is successfully indexed.');
-        req.flash('successMessage', 'Data is successfully indexed.');
-      }
-      else {
-        debug('Data index error.', data.errors);
-        req.flash('errorMessage', `Data index error: ${data.errors}`);
-      }
-      return res.redirect('/admin/search');
-    })
-    .catch(function(err) {
+    return res.render('admin/search', {});
+  };
+
+  actions.search.buildIndex = async function(req, res) {
+    const search = crowi.getSearcher();
+    if (!search) {
+      return res.redirect('/admin');
+    }
+
+    // first, delete index
+    try {
+      await search.deleteIndex();
+      debug('Index deleted.');
+    }
+    catch (err) {
+      debug('Delete index Error, but if it is initialize, its ok.', err);
+    }
+
+    // second, create index
+    try {
+      await search.buildIndex();
+      debug('Index created.');
+    }
+    catch (err) {
       debug('Error', err);
-      req.flash('errorMessage', `Error: ${err}`);
+      req.flash('errorMessage', 'Error while building index.');
       return res.redirect('/admin/search');
+    }
+
+    searchEvent.on('addPageProgress', (total, current, skip) => {
+      crowi.getIo().sockets.emit('admin:addPageProgress', { total, current, skip });
     });
-  };
+    searchEvent.on('finishAddPage', (total, current, skip) => {
+      crowi.getIo().sockets.emit('admin:finishAddPage', { total, current, skip });
+    });
+    // add all page
+    search
+      .addAllPages()
+      .then(() => {
+        debug('Data is successfully indexed. ------------------ ✧✧')
+      })
+      .catch(err => {
+        debug('Error', err);
+      });
+
+    req.flash('successMessage', 'Now re-building index ... this takes a while.')
+    return res.redirect('/admin/search');
+  }
 
   actions.user = {};
   actions.user.index = async function(req, res) {

+ 40 - 25
src/server/routes/search.js

@@ -1,17 +1,16 @@
 module.exports = function(crowi, app) {
   'use strict';
 
-  var debug = require('debug')('growi:routes:search')
-    , Page = crowi.model('Page')
-    , User = crowi.model('User')
-    , ApiResponse = require('../util/apiResponse')
-
-    , actions = {};
-  var api = actions.api = {};
+  // var debug = require('debug')('growi:routes:search')
+  const Page = crowi.model('Page');
+  const ApiResponse = require('../util/apiResponse');
+  const ApiPaginate = require('../util/apiPaginate');
+  const actions = {};
+  const api = (actions.api = {});
 
   actions.searchPage = function(req, res) {
-    var keyword = req.query.q || null;
-    var search = crowi.getSearcher();
+    const keyword = req.query.q || null;
+    const search = crowi.getSearcher();
     if (!search) {
       return res.json(ApiResponse.error('Configuration of ELASTICSEARCH_URI is required.'));
     }
@@ -28,40 +27,57 @@ module.exports = function(crowi, app) {
    *
    * @apiParam {String} q keyword
    * @apiParam {String} path
+   * @apiParam {String} offset
+   * @apiParam {String} limit
    */
   api.search = function(req, res) {
-    var keyword = req.query.q || null;
-    var tree = req.query.tree || null;
+    const { q: keyword = null, tree = null, type = null } = req.query;
+    let paginateOpts;
+
+    try {
+      paginateOpts = ApiPaginate.parseOptionsForElasticSearch(req.query);
+    }
+    catch (e) {
+      res.json(ApiResponse.error(e));
+    }
+
     if (keyword === null || keyword === '') {
       return res.json(ApiResponse.error('keyword should not empty.'));
     }
 
-    var search = crowi.getSearcher();
+    const search = crowi.getSearcher();
     if (!search) {
       return res.json(ApiResponse.error('Configuration of ELASTICSEARCH_URI is required.'));
     }
 
-
-    var doSearch;
+    const searchOpts = { ...paginateOpts, type };
+    let doSearch;
     if (tree) {
-      doSearch = search.searchKeywordUnderPath(keyword, tree, {});
+      doSearch = search.searchKeywordUnderPath(keyword, tree, searchOpts);
     }
     else {
-      doSearch = search.searchKeyword(keyword, {});
+      doSearch = search.searchKeyword(keyword, searchOpts);
     }
-    var result = {};
+    const result = {};
     doSearch
       .then(function(data) {
         result.meta = data.meta;
+        result.searchResult = data.data;
 
         return Page.populatePageListToAnyObjects(data.data);
-      }).then(function(pages) {
-        result.data = pages.filter(function(page) {
-          if (Object.keys(page).length < 12) { // FIXME: 12 is a number of columns.
-            return false;
-          }
-          return true;
-        });
+      })
+      .then(function(pages) {
+        result.data = pages
+          .filter(page => {
+            if (Object.keys(page).length < 12) {
+              // FIXME: 12 is a number of columns.
+              return false;
+            }
+            return true;
+          })
+          .map(page => {
+            return { ...page, bookmarkCount: (page._source && page._source.bookmark_count) || 0 };
+          });
         return res.json(ApiResponse.success(result));
       })
       .catch(function(err) {
@@ -69,6 +85,5 @@ module.exports = function(crowi, app) {
       });
   };
 
-
   return actions;
 };