query.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /**
  2. * A lunr.Query provides a programmatic way of defining queries to be performed
  3. * against a {@link lunr.Index}.
  4. *
  5. * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method
  6. * so the query object is pre-initialized with the right index fields.
  7. *
  8. * @constructor
  9. * @property {lunr.Query~Clause[]} clauses - An array of query clauses.
  10. * @property {string[]} allFields - An array of all available fields in a lunr.Index.
  11. */
  12. lunr.Query = function (allFields) {
  13. this.clauses = []
  14. this.allFields = allFields
  15. }
  16. /**
  17. * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause.
  18. *
  19. * This allows wildcards to be added to the beginning and end of a term without having to manually do any string
  20. * concatenation.
  21. *
  22. * The wildcard constants can be bitwise combined to select both leading and trailing wildcards.
  23. *
  24. * @constant
  25. * @default
  26. * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour
  27. * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists
  28. * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists
  29. * @see lunr.Query~Clause
  30. * @see lunr.Query#clause
  31. * @see lunr.Query#term
  32. * @example <caption>query term with trailing wildcard</caption>
  33. * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING })
  34. * @example <caption>query term with leading and trailing wildcard</caption>
  35. * query.term('foo', {
  36. * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING
  37. * })
  38. */
  39. lunr.Query.wildcard = new String ("*")
  40. lunr.Query.wildcard.NONE = 0
  41. lunr.Query.wildcard.LEADING = 1
  42. lunr.Query.wildcard.TRAILING = 2
  43. /**
  44. * Constants for indicating what kind of presence a term must have in matching documents.
  45. *
  46. * @constant
  47. * @enum {number}
  48. * @see lunr.Query~Clause
  49. * @see lunr.Query#clause
  50. * @see lunr.Query#term
  51. * @example <caption>query term with required presence</caption>
  52. * query.term('foo', { presence: lunr.Query.presence.REQUIRED })
  53. */
  54. lunr.Query.presence = {
  55. /**
  56. * Term's presence in a document is optional, this is the default value.
  57. */
  58. OPTIONAL: 1,
  59. /**
  60. * Term's presence in a document is required, documents that do not contain
  61. * this term will not be returned.
  62. */
  63. REQUIRED: 2,
  64. /**
  65. * Term's presence in a document is prohibited, documents that do contain
  66. * this term will not be returned.
  67. */
  68. PROHIBITED: 3
  69. }
  70. /**
  71. * A single clause in a {@link lunr.Query} contains a term and details on how to
  72. * match that term against a {@link lunr.Index}.
  73. *
  74. * @typedef {Object} lunr.Query~Clause
  75. * @property {string[]} fields - The fields in an index this clause should be matched against.
  76. * @property {number} [boost=1] - Any boost that should be applied when matching this clause.
  77. * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be.
  78. * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline.
  79. * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended.
  80. * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents.
  81. */
  82. /**
  83. * Adds a {@link lunr.Query~Clause} to this query.
  84. *
  85. * Unless the clause contains the fields to be matched all fields will be matched. In addition
  86. * a default boost of 1 is applied to the clause.
  87. *
  88. * @param {lunr.Query~Clause} clause - The clause to add to this query.
  89. * @see lunr.Query~Clause
  90. * @returns {lunr.Query}
  91. */
  92. lunr.Query.prototype.clause = function (clause) {
  93. if (!('fields' in clause)) {
  94. clause.fields = this.allFields
  95. }
  96. if (!('boost' in clause)) {
  97. clause.boost = 1
  98. }
  99. if (!('usePipeline' in clause)) {
  100. clause.usePipeline = true
  101. }
  102. if (!('wildcard' in clause)) {
  103. clause.wildcard = lunr.Query.wildcard.NONE
  104. }
  105. if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) {
  106. clause.term = "*" + clause.term
  107. }
  108. if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) {
  109. clause.term = "" + clause.term + "*"
  110. }
  111. if (!('presence' in clause)) {
  112. clause.presence = lunr.Query.presence.OPTIONAL
  113. }
  114. this.clauses.push(clause)
  115. return this
  116. }
  117. /**
  118. * A negated query is one in which every clause has a presence of
  119. * prohibited. These queries require some special processing to return
  120. * the expected results.
  121. *
  122. * @returns boolean
  123. */
  124. lunr.Query.prototype.isNegated = function () {
  125. for (var i = 0; i < this.clauses.length; i++) {
  126. if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) {
  127. return false
  128. }
  129. }
  130. return true
  131. }
  132. /**
  133. * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause}
  134. * to the list of clauses that make up this query.
  135. *
  136. * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion
  137. * to a token or token-like string should be done before calling this method.
  138. *
  139. * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an
  140. * array, each term in the array will share the same options.
  141. *
  142. * @param {object|object[]} term - The term(s) to add to the query.
  143. * @param {object} [options] - Any additional properties to add to the query clause.
  144. * @returns {lunr.Query}
  145. * @see lunr.Query#clause
  146. * @see lunr.Query~Clause
  147. * @example <caption>adding a single term to a query</caption>
  148. * query.term("foo")
  149. * @example <caption>adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard</caption>
  150. * query.term("foo", {
  151. * fields: ["title"],
  152. * boost: 10,
  153. * wildcard: lunr.Query.wildcard.TRAILING
  154. * })
  155. * @example <caption>using lunr.tokenizer to convert a string to tokens before using them as terms</caption>
  156. * query.term(lunr.tokenizer("foo bar"))
  157. */
  158. lunr.Query.prototype.term = function (term, options) {
  159. if (Array.isArray(term)) {
  160. term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this)
  161. return this
  162. }
  163. var clause = options || {}
  164. clause.term = term.toString()
  165. this.clause(clause)
  166. return this
  167. }