/** * A lunr.Query provides a programmatic way of defining queries to be performed * against a {@link lunr.Index}. * * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method * so the query object is pre-initialized with the right index fields. * * @constructor * @property {lunr.Query~Clause[]} clauses - An array of query clauses. * @property {string[]} allFields - An array of all available fields in a lunr.Index. */ lunr.Query = function (allFields) { this.clauses = [] this.allFields = allFields } /** * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause. * * This allows wildcards to be added to the beginning and end of a term without having to manually do any string * concatenation. * * The wildcard constants can be bitwise combined to select both leading and trailing wildcards. * * @constant * @default * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists * @see lunr.Query~Clause * @see lunr.Query#clause * @see lunr.Query#term * @example query term with trailing wildcard * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING }) * @example query term with leading and trailing wildcard * query.term('foo', { * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING * }) */ lunr.Query.wildcard = new String ("*") lunr.Query.wildcard.NONE = 0 lunr.Query.wildcard.LEADING = 1 lunr.Query.wildcard.TRAILING = 2 /** * Constants for indicating what kind of presence a term must have in matching documents. * * @constant * @enum {number} * @see lunr.Query~Clause * @see lunr.Query#clause * @see lunr.Query#term * @example query term with required presence * query.term('foo', { presence: lunr.Query.presence.REQUIRED }) */ lunr.Query.presence = { /** * Term's presence in a document is optional, this is the default value. */ OPTIONAL: 1, /** * Term's presence in a document is required, documents that do not contain * this term will not be returned. */ REQUIRED: 2, /** * Term's presence in a document is prohibited, documents that do contain * this term will not be returned. */ PROHIBITED: 3 } /** * A single clause in a {@link lunr.Query} contains a term and details on how to * match that term against a {@link lunr.Index}. * * @typedef {Object} lunr.Query~Clause * @property {string[]} fields - The fields in an index this clause should be matched against. * @property {number} [boost=1] - Any boost that should be applied when matching this clause. * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be. * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline. * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended. * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents. */ /** * Adds a {@link lunr.Query~Clause} to this query. * * Unless the clause contains the fields to be matched all fields will be matched. In addition * a default boost of 1 is applied to the clause. * * @param {lunr.Query~Clause} clause - The clause to add to this query. * @see lunr.Query~Clause * @returns {lunr.Query} */ lunr.Query.prototype.clause = function (clause) { if (!('fields' in clause)) { clause.fields = this.allFields } if (!('boost' in clause)) { clause.boost = 1 } if (!('usePipeline' in clause)) { clause.usePipeline = true } if (!('wildcard' in clause)) { clause.wildcard = lunr.Query.wildcard.NONE } if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) { clause.term = "*" + clause.term } if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) { clause.term = "" + clause.term + "*" } if (!('presence' in clause)) { clause.presence = lunr.Query.presence.OPTIONAL } this.clauses.push(clause) return this } /** * A negated query is one in which every clause has a presence of * prohibited. These queries require some special processing to return * the expected results. * * @returns boolean */ lunr.Query.prototype.isNegated = function () { for (var i = 0; i < this.clauses.length; i++) { if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) { return false } } return true } /** * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause} * to the list of clauses that make up this query. * * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion * to a token or token-like string should be done before calling this method. * * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an * array, each term in the array will share the same options. * * @param {object|object[]} term - The term(s) to add to the query. * @param {object} [options] - Any additional properties to add to the query clause. * @returns {lunr.Query} * @see lunr.Query#clause * @see lunr.Query~Clause * @example adding a single term to a query * query.term("foo") * @example adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard * query.term("foo", { * fields: ["title"], * boost: 10, * wildcard: lunr.Query.wildcard.TRAILING * }) * @example using lunr.tokenizer to convert a string to tokens before using them as terms * query.term(lunr.tokenizer("foo bar")) */ lunr.Query.prototype.term = function (term, options) { if (Array.isArray(term)) { term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this) return this } var clause = options || {} clause.term = term.toString() this.clause(clause) return this }