pipeline.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*!
  2. * lunr.Pipeline
  3. * Copyright (C) @YEAR Oliver Nightingale
  4. */
  5. /**
  6. * lunr.Pipelines maintain an ordered list of functions to be applied to all
  7. * tokens in documents entering the search index and queries being ran against
  8. * the index.
  9. *
  10. * An instance of lunr.Index created with the lunr shortcut will contain a
  11. * pipeline with a stop word filter and an English language stemmer. Extra
  12. * functions can be added before or after either of these functions or these
  13. * default functions can be removed.
  14. *
  15. * When run the pipeline will call each function in turn, passing a token, the
  16. * index of that token in the original list of all tokens and finally a list of
  17. * all the original tokens.
  18. *
  19. * The output of functions in the pipeline will be passed to the next function
  20. * in the pipeline. To exclude a token from entering the index the function
  21. * should return undefined, the rest of the pipeline will not be called with
  22. * this token.
  23. *
  24. * For serialisation of pipelines to work, all functions used in an instance of
  25. * a pipeline should be registered with lunr.Pipeline. Registered functions can
  26. * then be loaded. If trying to load a serialised pipeline that uses functions
  27. * that are not registered an error will be thrown.
  28. *
  29. * If not planning on serialising the pipeline then registering pipeline functions
  30. * is not necessary.
  31. *
  32. * @constructor
  33. */
  34. lunr.Pipeline = function () {
  35. this._stack = []
  36. }
  37. lunr.Pipeline.registeredFunctions = {}
  38. /**
  39. * Register a function with the pipeline.
  40. *
  41. * Functions that are used in the pipeline should be registered if the pipeline
  42. * needs to be serialised, or a serialised pipeline needs to be loaded.
  43. *
  44. * Registering a function does not add it to a pipeline, functions must still be
  45. * added to instances of the pipeline for them to be used when running a pipeline.
  46. *
  47. * @param {Function} fn The function to check for.
  48. * @param {String} label The label to register this function with
  49. * @memberOf Pipeline
  50. */
  51. lunr.Pipeline.registerFunction = function (fn, label) {
  52. if (label in this.registeredFunctions) {
  53. lunr.utils.warn('Overwriting existing registered function: ' + label)
  54. }
  55. fn.label = label
  56. lunr.Pipeline.registeredFunctions[fn.label] = fn
  57. }
  58. /**
  59. * Warns if the function is not registered as a Pipeline function.
  60. *
  61. * @param {Function} fn The function to check for.
  62. * @private
  63. * @memberOf Pipeline
  64. */
  65. lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
  66. var isRegistered = fn.label && (fn.label in this.registeredFunctions)
  67. if (!isRegistered) {
  68. lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn)
  69. }
  70. }
  71. /**
  72. * Loads a previously serialised pipeline.
  73. *
  74. * All functions to be loaded must already be registered with lunr.Pipeline.
  75. * If any function from the serialised data has not been registered then an
  76. * error will be thrown.
  77. *
  78. * @param {Object} serialised The serialised pipeline to load.
  79. * @returns {lunr.Pipeline}
  80. * @memberOf Pipeline
  81. */
  82. lunr.Pipeline.load = function (serialised) {
  83. var pipeline = new lunr.Pipeline
  84. serialised.forEach(function (fnName) {
  85. var fn = lunr.Pipeline.registeredFunctions[fnName]
  86. if (fn) {
  87. pipeline.add(fn)
  88. } else {
  89. throw new Error('Cannot load un-registered function: ' + fnName)
  90. }
  91. })
  92. return pipeline
  93. }
  94. /**
  95. * Adds new functions to the end of the pipeline.
  96. *
  97. * Logs a warning if the function has not been registered.
  98. *
  99. * @param {Function} functions Any number of functions to add to the pipeline.
  100. * @memberOf Pipeline
  101. */
  102. lunr.Pipeline.prototype.add = function () {
  103. var fns = Array.prototype.slice.call(arguments)
  104. fns.forEach(function (fn) {
  105. lunr.Pipeline.warnIfFunctionNotRegistered(fn)
  106. this._stack.push(fn)
  107. }, this)
  108. }
  109. /**
  110. * Adds a single function after a function that already exists in the
  111. * pipeline.
  112. *
  113. * Logs a warning if the function has not been registered.
  114. *
  115. * @param {Function} existingFn A function that already exists in the pipeline.
  116. * @param {Function} newFn The new function to add to the pipeline.
  117. * @memberOf Pipeline
  118. */
  119. lunr.Pipeline.prototype.after = function (existingFn, newFn) {
  120. lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
  121. var pos = this._stack.indexOf(existingFn)
  122. if (pos == -1) {
  123. throw new Error('Cannot find existingFn')
  124. }
  125. pos = pos + 1
  126. this._stack.splice(pos, 0, newFn)
  127. }
  128. /**
  129. * Adds a single function before a function that already exists in the
  130. * pipeline.
  131. *
  132. * Logs a warning if the function has not been registered.
  133. *
  134. * @param {Function} existingFn A function that already exists in the pipeline.
  135. * @param {Function} newFn The new function to add to the pipeline.
  136. * @memberOf Pipeline
  137. */
  138. lunr.Pipeline.prototype.before = function (existingFn, newFn) {
  139. lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
  140. var pos = this._stack.indexOf(existingFn)
  141. if (pos == -1) {
  142. throw new Error('Cannot find existingFn')
  143. }
  144. this._stack.splice(pos, 0, newFn)
  145. }
  146. /**
  147. * Removes a function from the pipeline.
  148. *
  149. * @param {Function} fn The function to remove from the pipeline.
  150. * @memberOf Pipeline
  151. */
  152. lunr.Pipeline.prototype.remove = function (fn) {
  153. var pos = this._stack.indexOf(fn)
  154. if (pos == -1) {
  155. return
  156. }
  157. this._stack.splice(pos, 1)
  158. }
  159. /**
  160. * Runs the current list of functions that make up the pipeline against the
  161. * passed tokens.
  162. *
  163. * @param {Array} tokens The tokens to run through the pipeline.
  164. * @returns {Array}
  165. * @memberOf Pipeline
  166. */
  167. lunr.Pipeline.prototype.run = function (tokens) {
  168. var out = [],
  169. tokenLength = tokens.length,
  170. stackLength = this._stack.length
  171. for (var i = 0; i < tokenLength; i++) {
  172. var token = tokens[i]
  173. for (var j = 0; j < stackLength; j++) {
  174. token = this._stack[j](token, i, tokens)
  175. if (token === void 0 || token === '') break
  176. };
  177. if (token !== void 0 && token !== '') out.push(token)
  178. };
  179. return out
  180. }
  181. /**
  182. * Resets the pipeline by removing any existing processors.
  183. *
  184. * @memberOf Pipeline
  185. */
  186. lunr.Pipeline.prototype.reset = function () {
  187. this._stack = []
  188. }
  189. /**
  190. * Returns a representation of the pipeline ready for serialisation.
  191. *
  192. * Logs a warning if the function has not been registered.
  193. *
  194. * @returns {Array}
  195. * @memberOf Pipeline
  196. */
  197. lunr.Pipeline.prototype.toJSON = function () {
  198. return this._stack.map(function (fn) {
  199. lunr.Pipeline.warnIfFunctionNotRegistered(fn)
  200. return fn.label
  201. })
  202. }