123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- /*!
- * lunr.Vector
- * Copyright (C) @YEAR Oliver Nightingale
- */
- /**
- * A vector is used to construct the vector space of documents and queries. These
- * vectors support operations to determine the similarity between two documents or
- * a document and a query.
- *
- * Normally no parameters are required for initializing a vector, but in the case of
- * loading a previously dumped vector the raw elements can be provided to the constructor.
- *
- * For performance reasons vectors are implemented with a flat array, where an elements
- * index is immediately followed by its value. E.g. [index, value, index, value]. This
- * allows the underlying array to be as sparse as possible and still offer decent
- * performance when being used for vector calculations.
- *
- * @constructor
- * @param {Number[]} [elements] - The flat list of element index and element value pairs.
- */
- lunr.Vector = function (elements) {
- this._magnitude = 0
- this.elements = elements || []
- }
- /**
- * Calculates the position within the vector to insert a given index.
- *
- * This is used internally by insert and upsert. If there are duplicate indexes then
- * the position is returned as if the value for that index were to be updated, but it
- * is the callers responsibility to check whether there is a duplicate at that index
- *
- * @param {Number} insertIdx - The index at which the element should be inserted.
- * @returns {Number}
- */
- lunr.Vector.prototype.positionForIndex = function (index) {
- // For an empty vector the tuple can be inserted at the beginning
- if (this.elements.length == 0) {
- return 0
- }
- var start = 0,
- end = this.elements.length / 2,
- sliceLength = end - start,
- pivotPoint = Math.floor(sliceLength / 2),
- pivotIndex = this.elements[pivotPoint * 2]
- while (sliceLength > 1) {
- if (pivotIndex < index) {
- start = pivotPoint
- }
- if (pivotIndex > index) {
- end = pivotPoint
- }
- if (pivotIndex == index) {
- break
- }
- sliceLength = end - start
- pivotPoint = start + Math.floor(sliceLength / 2)
- pivotIndex = this.elements[pivotPoint * 2]
- }
- if (pivotIndex == index) {
- return pivotPoint * 2
- }
- if (pivotIndex > index) {
- return pivotPoint * 2
- }
- if (pivotIndex < index) {
- return (pivotPoint + 1) * 2
- }
- }
- /**
- * Inserts an element at an index within the vector.
- *
- * Does not allow duplicates, will throw an error if there is already an entry
- * for this index.
- *
- * @param {Number} insertIdx - The index at which the element should be inserted.
- * @param {Number} val - The value to be inserted into the vector.
- */
- lunr.Vector.prototype.insert = function (insertIdx, val) {
- this.upsert(insertIdx, val, function () {
- throw "duplicate index"
- })
- }
- /**
- * Inserts or updates an existing index within the vector.
- *
- * @param {Number} insertIdx - The index at which the element should be inserted.
- * @param {Number} val - The value to be inserted into the vector.
- * @param {function} fn - A function that is called for updates, the existing value and the
- * requested value are passed as arguments
- */
- lunr.Vector.prototype.upsert = function (insertIdx, val, fn) {
- this._magnitude = 0
- var position = this.positionForIndex(insertIdx)
- if (this.elements[position] == insertIdx) {
- this.elements[position + 1] = fn(this.elements[position + 1], val)
- } else {
- this.elements.splice(position, 0, insertIdx, val)
- }
- }
- /**
- * Calculates the magnitude of this vector.
- *
- * @returns {Number}
- */
- lunr.Vector.prototype.magnitude = function () {
- if (this._magnitude) return this._magnitude
- var sumOfSquares = 0,
- elementsLength = this.elements.length
- for (var i = 1; i < elementsLength; i += 2) {
- var val = this.elements[i]
- sumOfSquares += val * val
- }
- return this._magnitude = Math.sqrt(sumOfSquares)
- }
- /**
- * Calculates the dot product of this vector and another vector.
- *
- * @param {lunr.Vector} otherVector - The vector to compute the dot product with.
- * @returns {Number}
- */
- lunr.Vector.prototype.dot = function (otherVector) {
- var dotProduct = 0,
- a = this.elements, b = otherVector.elements,
- aLen = a.length, bLen = b.length,
- aVal = 0, bVal = 0,
- i = 0, j = 0
- while (i < aLen && j < bLen) {
- aVal = a[i], bVal = b[j]
- if (aVal < bVal) {
- i += 2
- } else if (aVal > bVal) {
- j += 2
- } else if (aVal == bVal) {
- dotProduct += a[i + 1] * b[j + 1]
- i += 2
- j += 2
- }
- }
- return dotProduct
- }
- /**
- * Calculates the similarity between this vector and another vector.
- *
- * @param {lunr.Vector} otherVector - The other vector to calculate the
- * similarity with.
- * @returns {Number}
- */
- lunr.Vector.prototype.similarity = function (otherVector) {
- return this.dot(otherVector) / this.magnitude() || 0
- }
- /**
- * Converts the vector to an array of the elements within the vector.
- *
- * @returns {Number[]}
- */
- lunr.Vector.prototype.toArray = function () {
- var output = new Array (this.elements.length / 2)
- for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) {
- output[j] = this.elements[i]
- }
- return output
- }
- /**
- * A JSON serializable representation of the vector.
- *
- * @returns {Number[]}
- */
- lunr.Vector.prototype.toJSON = function () {
- return this.elements
- }
|