Comparing version 2.0.2 to 2.0.3
@@ -27,3 +27,4 @@ { | ||
"whitespace", "automata", "i", "obj", "anymore", "lexer", "var", "refs", | ||
"serializable", "tis", "twas", "int", "args", "unshift", "plugins" | ||
"serializable", "tis", "twas", "int", "args", "unshift", "plugins", "upsert", | ||
"upserting" | ||
] | ||
@@ -30,0 +31,0 @@ } |
@@ -197,3 +197,3 @@ /*! | ||
/* | ||
* Inserting the found query term, along with its term index | ||
* Upserting the found query term, along with its term index | ||
* into the vector representing the query. It is here that | ||
@@ -203,4 +203,8 @@ * any boosts are applied to the score. They could have been | ||
* is already quite busy. | ||
* | ||
* Using upsert because there could already be an entry in the vector | ||
* for the term we are working with. In that case we just add the scores | ||
* together. | ||
*/ | ||
queryVector.insert(termIndex, score * clause.boost) | ||
queryVector.upsert(termIndex, score * clause.boost, function (a, b) { return a + b }) | ||
@@ -207,0 +211,0 @@ for (var k = 0; k < clause.fields.length; k++) { |
@@ -27,50 +27,91 @@ /*! | ||
/** | ||
* Inserts an element at an index within the vector. | ||
* 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. | ||
* @param {Number} val - The value to be inserted into the vector. | ||
* @returns {Number} | ||
*/ | ||
lunr.Vector.prototype.insert = function (insertIdx, val) { | ||
this._magnitude = 0 | ||
lunr.Vector.prototype.positionForIndex = function (index) { | ||
// For an empty vector the tuple can be inserted at the beginning | ||
if (this.elements.length == 0) { | ||
this.elements.push(insertIdx, val) | ||
return | ||
return 0 | ||
} | ||
var start = 0, | ||
end = this.elements.length, | ||
end = this.elements.length / 2, | ||
sliceLength = end - start, | ||
pivot = Math.floor((sliceLength / 2) / 2) * 2, | ||
pivotIdx = this.elements[pivot] | ||
pivotPoint = Math.floor(sliceLength / 2), | ||
pivotIndex = this.elements[pivotPoint * 2] | ||
while (sliceLength > 2) { | ||
if (pivotIdx == insertIdx) { | ||
throw "duplicate index" | ||
while (sliceLength > 1) { | ||
if (pivotIndex < index) { | ||
start = pivotPoint | ||
} | ||
if (insertIdx > pivotIdx) { | ||
start = pivot | ||
if (pivotIndex > index) { | ||
end = pivotPoint | ||
} | ||
if (insertIdx < pivotIdx) { | ||
end = pivot | ||
if (pivotIndex == index) { | ||
break | ||
} | ||
sliceLength = end - start | ||
pivot = start + Math.floor((sliceLength / 2) / 2) * 2 | ||
pivotIdx = this.elements[pivot] | ||
pivotPoint = start + Math.floor(sliceLength / 2) | ||
pivotIndex = this.elements[pivotPoint * 2] | ||
} | ||
if (pivotIdx > insertIdx) { | ||
this.elements.splice(pivot, 0, insertIdx, val) | ||
if (pivotIndex == index) { | ||
return pivotPoint * 2 | ||
} | ||
if (pivotIdx < insertIdx) { | ||
this.elements.splice(pivot + 2, 0, insertIdx, val) | ||
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. | ||
@@ -77,0 +118,0 @@ * |
{ | ||
"name": "lunr", | ||
"description": "Simple full-text search in your browser.", | ||
"version": "2.0.2", | ||
"version": "2.0.3", | ||
"author": "Oliver Nightingale", | ||
@@ -6,0 +6,0 @@ "keywords": ["search"], |
@@ -147,2 +147,15 @@ suite('search', function () { | ||
suite('duplicate query terms', function () { | ||
// https://github.com/olivernn/lunr.js/issues/256 | ||
// previously this would throw a duplicate index error | ||
// because the query vector already contained an entry | ||
// for the term 'fellow' | ||
test('no errors', function () { | ||
var idx = this.idx | ||
assert.doesNotThrow(function () { | ||
idx.search('fellow candlestick foo bar green plant fellow') | ||
}) | ||
}) | ||
}) | ||
suite('documents with all terms score higher', function () { | ||
@@ -149,0 +162,0 @@ setup(function () { |
@@ -58,3 +58,82 @@ suite('lunr.Vector', function () { | ||
}) | ||
test('fails when duplicate entry', function () { | ||
var vector = vectorFromArgs(4, 5, 6) | ||
assert.throws(function () { vector.insert(0, 44) }) | ||
}) | ||
}) | ||
suite('#upsert', function () { | ||
test('invalidates magnitude cache', function () { | ||
var vector = vectorFromArgs(4,5,6) | ||
assert.equal(Math.sqrt(77), vector.magnitude()) | ||
vector.upsert(3, 7) | ||
assert.equal(Math.sqrt(126), vector.magnitude()) | ||
}) | ||
test('keeps items in index specified order', function () { | ||
var vector = new lunr.Vector | ||
vector.upsert(2, 4) | ||
vector.upsert(1, 5) | ||
vector.upsert(0, 6) | ||
assert.deepEqual([6,5,4], vector.toArray()) | ||
}) | ||
test('calls fn for value on duplicate', function () { | ||
var vector = vectorFromArgs(4, 5, 6) | ||
vector.upsert(0, 4, function (current, passed) { return current + passed }) | ||
assert.deepEqual([8, 5, 6], vector.toArray()) | ||
}) | ||
}) | ||
suite('#positionForIndex', function () { | ||
var vector = new lunr.Vector ([ | ||
1, 'a', | ||
2, 'b', | ||
4, 'c', | ||
7, 'd', | ||
11, 'e' | ||
]) | ||
test('at the beginning', function () { | ||
assert.equal(0, vector.positionForIndex(0)) | ||
}) | ||
test('at the end', function () { | ||
assert.equal(10, vector.positionForIndex(20)) | ||
}) | ||
test('consecutive', function () { | ||
assert.equal(4, vector.positionForIndex(3)) | ||
}) | ||
test('non-consecutive gap after', function () { | ||
assert.equal(6, vector.positionForIndex(5)) | ||
}) | ||
test('non-consecutive gap before', function () { | ||
assert.equal(6, vector.positionForIndex(6)) | ||
}) | ||
test('non-consecutive gave before and after', function () { | ||
assert.equal(8, vector.positionForIndex(9)) | ||
}) | ||
test('duplicate at the beginning', function () { | ||
assert.equal(0, vector.positionForIndex(1)) | ||
}) | ||
test('duplicate at the end', function () { | ||
assert.equal(8, vector.positionForIndex(11)) | ||
}) | ||
test('duplicate consecutive', function () { | ||
assert.equal(4, vector.positionForIndex(4)) | ||
}) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
579503
18240