Comparing version 2.2.0-alpha.1 to 2.2.0-alpha.2
@@ -147,2 +147,11 @@ /*! | ||
/* | ||
* To support field level boosts a query vector is created per | ||
* field. An empty vector is eagerly created to support negated | ||
* queries. | ||
*/ | ||
for (var i = 0; i < this.fields.length; i++) { | ||
queryVectors[this.fields[i]] = new lunr.Vector | ||
} | ||
fn.call(query, query) | ||
@@ -260,14 +269,4 @@ | ||
/* | ||
* To support field level boosts a query vector is created per | ||
* field. This vector is populated using the termIndex found for | ||
* The query field vector is populated using the termIndex found for | ||
* the term and a unit value with the appropriate boost applied. | ||
* | ||
* If the query vector for this field does not exist yet it needs | ||
* to be created. | ||
*/ | ||
if (queryVectors[field] === undefined) { | ||
queryVectors[field] = new lunr.Vector | ||
} | ||
/* | ||
* Using upsert because there could already be an entry in the vector | ||
@@ -337,2 +336,22 @@ * for the term we are working with. In that case we just add the scores | ||
/* | ||
* If the query is negated (contains only prohibited terms) | ||
* we need to get _all_ fieldRefs currently existing in the | ||
* index. This is only done when we know that the query is | ||
* entirely prohibited terms to avoid any cost of getting all | ||
* fieldRefs unnecessarily. | ||
* | ||
* Additionally, blank MatchData must be created to correctly | ||
* populate the results. | ||
*/ | ||
if (query.isNegated()) { | ||
matchingFieldRefs = Object.keys(this.fieldVectors) | ||
for (var i = 0; i < matchingFieldRefs.length; i++) { | ||
var matchingFieldRef = matchingFieldRefs[i] | ||
var fieldRef = lunr.FieldRef.fromString(matchingFieldRef) | ||
matchingFields[matchingFieldRef] = new lunr.MatchData | ||
} | ||
} | ||
for (var i = 0; i < matchingFieldRefs.length; i++) { | ||
@@ -348,6 +367,3 @@ /* | ||
var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]), | ||
docRef = fieldRef.docRef, | ||
fieldVector = this.fieldVectors[fieldRef], | ||
score = queryVectors[fieldRef.fieldName].similarity(fieldVector), | ||
docMatch | ||
docRef = fieldRef.docRef | ||
@@ -362,2 +378,6 @@ if (!allRequiredMatches.contains(docRef)) { | ||
var fieldVector = this.fieldVectors[fieldRef], | ||
score = queryVectors[fieldRef.fieldName].similarity(fieldVector), | ||
docMatch | ||
if ((docMatch = matches[docRef]) !== undefined) { | ||
@@ -364,0 +384,0 @@ docMatch.score += score |
@@ -15,3 +15,3 @@ /** | ||
var clonedMetadata = Object.create(null), | ||
metadataKeys = Object.keys(metadata) | ||
metadataKeys = Object.keys(metadata || {}) | ||
@@ -29,4 +29,7 @@ // Cloning the metadata to prevent the original | ||
this.metadata = Object.create(null) | ||
this.metadata[term] = Object.create(null) | ||
this.metadata[term][field] = clonedMetadata | ||
if (term !== undefined) { | ||
this.metadata[term] = Object.create(null) | ||
this.metadata[term][field] = clonedMetadata | ||
} | ||
} | ||
@@ -33,0 +36,0 @@ |
@@ -134,2 +134,19 @@ /** | ||
/** | ||
* 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} | ||
@@ -136,0 +153,0 @@ * to the list of clauses that make up this query. |
@@ -172,3 +172,3 @@ /*! | ||
lunr.Vector.prototype.similarity = function (otherVector) { | ||
return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude()) | ||
return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude()) || 0 | ||
} | ||
@@ -175,0 +175,0 @@ |
{ | ||
"name": "lunr", | ||
"description": "Simple full-text search in your browser.", | ||
"version": "2.2.0-alpha.1", | ||
"version": "2.2.0-alpha.2", | ||
"author": "Oliver Nightingale", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
@@ -64,2 +64,10 @@ suite('search', function () { | ||
}) | ||
this.add('negated query', function () { | ||
idx.search('-plant') | ||
}) | ||
this.add('required term', function () { | ||
idx.search('green +plant') | ||
}) | ||
}) |
@@ -130,2 +130,41 @@ suite('lunr.Query', function () { | ||
}) | ||
suite('#isNegated', function () { | ||
setup(function () { | ||
this.query = new lunr.Query (allFields) | ||
}) | ||
suite('all prohibited', function () { | ||
setup(function () { | ||
this.query.term('foo', { presence: lunr.Query.presence.PROHIBITED }) | ||
this.query.term('bar', { presence: lunr.Query.presence.PROHIBITED }) | ||
}) | ||
test('is negated', function () { | ||
assert.isTrue(this.query.isNegated()) | ||
}) | ||
}) | ||
suite('some prohibited', function () { | ||
setup(function () { | ||
this.query.term('foo', { presence: lunr.Query.presence.PROHIBITED }) | ||
this.query.term('bar', { presence: lunr.Query.presence.REQUIRED }) | ||
}) | ||
test('is negated', function () { | ||
assert.isFalse(this.query.isNegated()) | ||
}) | ||
}) | ||
suite('none prohibited', function () { | ||
setup(function () { | ||
this.query.term('foo', { presence: lunr.Query.presence.OPTIONAL }) | ||
this.query.term('bar', { presence: lunr.Query.presence.REQUIRED }) | ||
}) | ||
test('is negated', function () { | ||
assert.isFalse(this.query.isNegated()) | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -610,17 +610,54 @@ suite('search', function () { | ||
suite('no matching term', function () { | ||
setup(function () { | ||
suite('negated query no match', function () { | ||
var assertions = function (fn) { | ||
setup(fn) | ||
test('all documents returned', function () { | ||
assert.lengthOf(this.results, 3) | ||
}) | ||
test('all results have same score', function () { | ||
assert.isTrue(this.results.every(function (r) { return r.score === 0 })) | ||
}) | ||
} | ||
suite('#query', assertions(function () { | ||
this.results = this.idx.query(function (q) { | ||
q.term('qwertyuiop', { presence: lunr.Query.presence.PROHIBITED }) | ||
}) | ||
}) | ||
})) | ||
// TODO: what is the right behaviour here? | ||
// logically it _should_ return all results, but is that useful? It would | ||
// require a bunch of extra work to return a result set that isn't very useful. | ||
// Perhaps instead it should just mark queries that only have prohibited clauses | ||
// as invalid? | ||
test('all results returned?') | ||
suite('#search', assertions(function () { | ||
this.results = this.idx.search("-qwertyuiop") | ||
})) | ||
}) | ||
suite('negated query some match', function () { | ||
var assertions = function (fn) { | ||
setup(fn) | ||
test('all documents returned', function () { | ||
assert.lengthOf(this.results, 1) | ||
}) | ||
test('all results have same score', function () { | ||
assert.isTrue(this.results.every(function (r) { return r.score === 0 })) | ||
}) | ||
test('matching documents returned', function () { | ||
assert.equal('a', this.results[0].ref) | ||
}) | ||
} | ||
suite('#query', assertions(function () { | ||
this.results = this.idx.query(function (q) { | ||
q.term('plant', { presence: lunr.Query.presence.PROHIBITED }) | ||
}) | ||
})) | ||
suite('#search', assertions(function () { | ||
this.results = this.idx.search("-plant") | ||
})) | ||
}) | ||
suite('field match', function () { | ||
@@ -627,0 +664,0 @@ var assertions = function (fn) { |
@@ -36,2 +36,18 @@ suite('lunr.Vector', function () { | ||
}) | ||
test('empty vector', function () { | ||
var vEmpty = new lunr.Vector, | ||
v1 = vectorFromArgs(1) | ||
assert.equal(0, vEmpty.similarity(v1)) | ||
assert.equal(0, v1.similarity(vEmpty)) | ||
}) | ||
test('non-overlapping vector', function () { | ||
var v1 = new lunr.Vector([1, 1]), | ||
v2 = new lunr.Vector([2, 1]) | ||
assert.equal(0, v1.similarity(v2)) | ||
assert.equal(0, v2.similarity(v1)) | ||
}) | ||
}) | ||
@@ -38,0 +54,0 @@ |
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
664876
20160