You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
6.0 KiB

5 years ago
5 years ago
  1. const _ = require('lodash')
  2. const { SearchService, QueryType } = require('azure-search-client')
  3. const request = require('request-promise')
  4. const stream = require('stream')
  5. const Promise = require('bluebird')
  6. const pipeline = Promise.promisify(stream.pipeline)
  7. /* global WIKI */
  8. module.exports = {
  9. async activate() {
  10. // not used
  11. },
  12. async deactivate() {
  13. // not used
  14. },
  15. /**
  16. * INIT
  17. */
  18. async init() {
  19. WIKI.logger.info(`(SEARCH/AZURE) Initializing...`)
  20. this.client = new SearchService(this.config.serviceName, this.config.adminKey)
  21. // -> Create Search Index
  22. const indexes = await this.client.indexes.list()
  23. if (!_.find(_.get(indexes, 'result.value', []), ['name', this.config.indexName])) {
  24. WIKI.logger.info(`(SEARCH/AZURE) Creating index...`)
  25. await this.client.indexes.create({
  26. name: this.config.indexName,
  27. fields: [
  28. {
  29. name: 'id',
  30. type: 'Edm.String',
  31. key: true,
  32. searchable: false
  33. },
  34. {
  35. name: 'locale',
  36. type: 'Edm.String',
  37. searchable: false
  38. },
  39. {
  40. name: 'path',
  41. type: 'Edm.String',
  42. searchable: false
  43. },
  44. {
  45. name: 'title',
  46. type: 'Edm.String',
  47. searchable: true
  48. },
  49. {
  50. name: 'description',
  51. type: 'Edm.String',
  52. searchable: true
  53. },
  54. {
  55. name: 'content',
  56. type: 'Edm.String',
  57. searchable: true
  58. }
  59. ],
  60. scoringProfiles: [
  61. {
  62. name: 'fieldWeights',
  63. text: {
  64. weights: {
  65. title: 4,
  66. description: 3,
  67. content: 1
  68. }
  69. }
  70. }
  71. ],
  72. suggesters: [
  73. {
  74. name: 'suggestions',
  75. searchMode: 'analyzingInfixMatching',
  76. sourceFields: ['title', 'description', 'content']
  77. }
  78. ]
  79. })
  80. }
  81. WIKI.logger.info(`(SEARCH/AZURE) Initialization completed.`)
  82. },
  83. /**
  84. * QUERY
  85. *
  86. * @param {String} q Query
  87. * @param {Object} opts Additional options
  88. */
  89. async query(q, opts) {
  90. try {
  91. let suggestions = []
  92. const results = await this.client.indexes.use(this.config.indexName).search({
  93. count: true,
  94. scoringProfile: 'fieldWeights',
  95. search: q,
  96. select: 'id, locale, path, title, description',
  97. queryType: QueryType.simple,
  98. top: 50
  99. })
  100. if (results.result.value.length < 5) {
  101. // Using plain request, not yet available in library...
  102. try {
  103. const suggestResults = await request({
  104. uri: `https://${this.config.serviceName}.search.windows.net/indexes/${this.config.indexName}/docs/autocomplete`,
  105. method: 'post',
  106. qs: {
  107. 'api-version': '2017-11-11-Preview'
  108. },
  109. headers: {
  110. 'api-key': this.config.adminKey,
  111. 'Content-Type': 'application/json'
  112. },
  113. json: true,
  114. body: {
  115. autocompleteMode: 'oneTermWithContext',
  116. search: q,
  117. suggesterName: 'suggestions'
  118. }
  119. })
  120. suggestions = suggestResults.value.map(s => s.queryPlusText)
  121. } catch (err) {
  122. WIKI.logger.warn('Search Engine suggestion failure: ', err)
  123. }
  124. }
  125. return {
  126. results: results.result.value,
  127. suggestions,
  128. totalHits: results.result['@odata.count']
  129. }
  130. } catch (err) {
  131. WIKI.logger.warn('Search Engine Error:')
  132. WIKI.logger.warn(err)
  133. }
  134. },
  135. /**
  136. * CREATE
  137. *
  138. * @param {Object} page Page to create
  139. */
  140. async created(page) {
  141. await this.client.indexes.use(this.config.indexName).index([
  142. {
  143. id: page.hash,
  144. locale: page.localeCode,
  145. path: page.path,
  146. title: page.title,
  147. description: page.description,
  148. content: page.safeContent
  149. }
  150. ])
  151. },
  152. /**
  153. * UPDATE
  154. *
  155. * @param {Object} page Page to update
  156. */
  157. async updated(page) {
  158. await this.client.indexes.use(this.config.indexName).index([
  159. {
  160. id: page.hash,
  161. locale: page.localeCode,
  162. path: page.path,
  163. title: page.title,
  164. description: page.description,
  165. content: page.safeContent
  166. }
  167. ])
  168. },
  169. /**
  170. * DELETE
  171. *
  172. * @param {Object} page Page to delete
  173. */
  174. async deleted(page) {
  175. await this.client.indexes.use(this.config.indexName).index([
  176. {
  177. '@search.action': 'delete',
  178. id: page.hash
  179. }
  180. ])
  181. },
  182. /**
  183. * RENAME
  184. *
  185. * @param {Object} page Page to rename
  186. */
  187. async renamed(page) {
  188. await this.client.indexes.use(this.config.indexName).index([
  189. {
  190. '@search.action': 'delete',
  191. id: page.hash
  192. }
  193. ])
  194. await this.client.indexes.use(this.config.indexName).index([
  195. {
  196. id: page.destinationHash,
  197. locale: page.destinationLocaleCode,
  198. path: page.destinationPath,
  199. title: page.title,
  200. description: page.description,
  201. content: page.safeContent
  202. }
  203. ])
  204. },
  205. /**
  206. * REBUILD INDEX
  207. */
  208. async rebuild() {
  209. WIKI.logger.info(`(SEARCH/AZURE) Rebuilding Index...`)
  210. await pipeline(
  211. WIKI.models.knex.column({ id: 'hash' }, 'path', { locale: 'localeCode' }, 'title', 'description', 'render').select().from('pages').where({
  212. isPublished: true,
  213. isPrivate: false
  214. }).stream(),
  215. new stream.Transform({
  216. objectMode: true,
  217. transform: (chunk, enc, cb) => {
  218. cb(null, {
  219. id: chunk.id,
  220. path: chunk.path,
  221. locale: chunk.locale,
  222. title: chunk.title,
  223. description: chunk.description,
  224. content: WIKI.models.pages.cleanHTML(chunk.render)
  225. })
  226. }
  227. }),
  228. this.client.indexes.use(this.config.indexName).createIndexingStream()
  229. )
  230. WIKI.logger.info(`(SEARCH/AZURE) Index rebuilt successfully.`)
  231. }
  232. }