| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 | 
							- 'use strict'
 
- /* global wiki */
 
- const Promise = require('bluebird')
 
- const _ = require('lodash')
 
- const searchIndex = require('./search-index')
 
- const stopWord = require('stopword')
 
- const streamToPromise = require('stream-to-promise')
 
- const searchAllowedChars = new RegExp('[^a-z0-9' + wiki.data.regex.cjk + wiki.data.regex.arabic + ' ]', 'g')
 
- module.exports = {
 
-   _si: null,
 
-   _isReady: false,
 
-   /**
 
-    * Initialize search index
 
-    *
 
-    * @return {undefined} Void
 
-    */
 
-   init () {
 
-     let self = this
 
-     self._isReady = new Promise((resolve, reject) => {
 
-       searchIndex({
 
-         deletable: true,
 
-         fieldedSearch: true,
 
-         indexPath: 'wiki',
 
-         logLevel: 'error',
 
-         stopwords: _.get(stopWord, wiki.config.lang, [])
 
-       }, (err, si) => {
 
-         if (err) {
 
-           wiki.logger.error('Failed to initialize search index.', err)
 
-           reject(err)
 
-         } else {
 
-           self._si = Promise.promisifyAll(si)
 
-           self._si.flushAsync().then(() => {
 
-             wiki.logger.info('Search index flushed and ready.')
 
-             resolve(true)
 
-           })
 
-         }
 
-       })
 
-     })
 
-     return self
 
-   },
 
-   /**
 
-    * Add a document to the index
 
-    *
 
-    * @param      {Object}   content  Document content
 
-    * @return     {Promise}  Promise of the add operation
 
-    */
 
-   add (content) {
 
-     let self = this
 
-     if (!content.isEntry) {
 
-       return Promise.resolve(true)
 
-     }
 
-     return self._isReady.then(() => {
 
-       return self.delete(content._id).then(() => {
 
-         return self._si.concurrentAddAsync({
 
-           fieldOptions: [{
 
-             fieldName: 'entryPath',
 
-             searchable: true,
 
-             weight: 2
 
-           },
 
-           {
 
-             fieldName: 'title',
 
-             nGramLength: [1, 2],
 
-             searchable: true,
 
-             weight: 3
 
-           },
 
-           {
 
-             fieldName: 'subtitle',
 
-             searchable: true,
 
-             weight: 1,
 
-             storeable: false
 
-           },
 
-           {
 
-             fieldName: 'parent',
 
-             searchable: false
 
-           },
 
-           {
 
-             fieldName: 'content',
 
-             searchable: true,
 
-             weight: 0,
 
-             storeable: false
 
-           }]
 
-         }, [{
 
-           entryPath: content._id,
 
-           title: content.title,
 
-           subtitle: content.subtitle || '',
 
-           parent: content.parent || '',
 
-           content: content.text || ''
 
-         }]).then(() => {
 
-           wiki.logger.log('verbose', 'Entry ' + content._id + ' added/updated to search index.')
 
-           return true
 
-         }).catch((err) => {
 
-           wiki.logger.error(err)
 
-         })
 
-       }).catch((err) => {
 
-         wiki.logger.error(err)
 
-       })
 
-     })
 
-   },
 
-   /**
 
-    * Delete an entry from the index
 
-    *
 
-    * @param      {String}   The     entry path
 
-    * @return     {Promise}  Promise of the operation
 
-    */
 
-   delete (entryPath) {
 
-     let self = this
 
-     return self._isReady.then(() => {
 
-       return streamToPromise(self._si.search({
 
-         query: [{
 
-           AND: { 'entryPath': [entryPath] }
 
-         }]
 
-       })).then((results) => {
 
-         if (results && results.length > 0) {
 
-           let delIds = _.map(results, 'id')
 
-           return self._si.delAsync(delIds)
 
-         } else {
 
-           return true
 
-         }
 
-       }).catch((err) => {
 
-         if (err.type === 'NotFoundError') {
 
-           return true
 
-         } else {
 
-           wiki.logger.error(err)
 
-         }
 
-       })
 
-     })
 
-   },
 
-   /**
 
-    * Flush the index
 
-    *
 
-    * @returns {Promise} Promise of the flush operation
 
-    */
 
-   flush () {
 
-     let self = this
 
-     return self._isReady.then(() => {
 
-       return self._si.flushAsync()
 
-     })
 
-   },
 
-   /**
 
-    * Search the index
 
-    *
 
-    * @param {Array<String>} terms
 
-    * @returns {Promise<Object>} Hits and suggestions
 
-    */
 
-   find (terms) {
 
-     let self = this
 
-     terms = _.chain(terms)
 
-       .deburr()
 
-       .toLower()
 
-       .trim()
 
-       .replace(searchAllowedChars, ' ')
 
-       .value()
 
-     let arrTerms = _.chain(terms)
 
-       .split(' ')
 
-       .filter((f) => { return !_.isEmpty(f) })
 
-       .value()
 
-     return streamToPromise(self._si.search({
 
-       query: [{
 
-         AND: { '*': arrTerms }
 
-       }],
 
-       pageSize: 10
 
-     })).then((hits) => {
 
-       if (hits.length > 0) {
 
-         hits = _.map(_.sortBy(hits, ['score']), h => {
 
-           return h.document
 
-         })
 
-       }
 
-       if (hits.length < 5) {
 
-         return streamToPromise(self._si.match({
 
-           beginsWith: terms,
 
-           threshold: 3,
 
-           limit: 5,
 
-           type: 'simple'
 
-         })).then((matches) => {
 
-           return {
 
-             match: hits,
 
-             suggest: matches
 
-           }
 
-         })
 
-       } else {
 
-         return {
 
-           match: hits,
 
-           suggest: []
 
-         }
 
-       }
 
-     }).catch((err) => {
 
-       if (err.type === 'NotFoundError') {
 
-         return {
 
-           match: [],
 
-           suggest: []
 
-         }
 
-       } else {
 
-         wiki.logger.error(err)
 
-       }
 
-     })
 
-   }
 
- }
 
 
  |