Comparing version 2.1.0-alpha.1 to 2.1.0-alpha.2
@@ -130,3 +130,3 @@ /*! | ||
matchingFields = Object.create(null), | ||
queryVector = new lunr.Vector | ||
queryVectors = Object.create(null) | ||
@@ -174,9 +174,4 @@ fn.call(query, query) | ||
/* | ||
* For each term calculate the score as the term relates to the | ||
* query using the same calculation used to score documents during | ||
* indexing. This score will be used to build a vector space | ||
* representation of the query. | ||
* | ||
* Also need to discover the terms index to insert into the query | ||
* vector at the right position | ||
* For each term get the posting and termIndex, this is required for | ||
* building the query vector. | ||
*/ | ||
@@ -187,15 +182,2 @@ var expandedTerm = expandedTerms[j], | ||
/* | ||
* Upserting the found query term, along with its term index | ||
* into the vector representing the query. It is here that | ||
* any boosts are applied to the score. They could have been | ||
* applied when calculating the score above, but that expression | ||
* 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.upsert(termIndex, 1 * clause.boost, function (a, b) { return a + b }) | ||
for (var k = 0; k < clause.fields.length; k++) { | ||
@@ -214,2 +196,21 @@ /* | ||
/* | ||
* To support field level boosts a query vector is created per | ||
* field. This 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 (!(field in queryVectors)) { | ||
queryVectors[field] = new lunr.Vector | ||
} | ||
/* | ||
* 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. | ||
*/ | ||
queryVectors[field].upsert(termIndex, 1 * clause.boost, function (a, b) { return a + b }) | ||
for (var l = 0; l < matchingDocumentRefs.length; l++) { | ||
@@ -246,11 +247,8 @@ /* | ||
/* | ||
* With all the matching documents found they now need | ||
* to be sorted by their relevance to the query. This | ||
* is done by retrieving the documents vector representation | ||
* and then finding its similarity with the query vector | ||
* that was constructed earlier. | ||
* Currently we have document fields that match the query, but we | ||
* need to return documents. The matchData and scores are combined | ||
* from multiple fields belonging to the same document. | ||
* | ||
* This score, along with the document ref and any metadata | ||
* we collected into a lunr.MatchData instance are stored | ||
* in the results array ready for returning to the caller | ||
* Scores are calculated by field, using the query vectors created | ||
* above, and combined into a final document score using addition. | ||
*/ | ||
@@ -260,3 +258,3 @@ var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]), | ||
fieldVector = this.fieldVectors[fieldRef], | ||
score = queryVector.similarity(fieldVector) | ||
score = queryVectors[fieldRef.fieldName].similarity(fieldVector) | ||
@@ -275,2 +273,6 @@ if (docRef in results) { | ||
/* | ||
* The results object needs to be converted into a list | ||
* of results, sorted by score before being returned. | ||
*/ | ||
return Object.keys(results) | ||
@@ -277,0 +279,0 @@ .map(function (key) { |
@@ -7,2 +7,3 @@ lunr.QueryLexer = function (str) { | ||
this.start = 0 | ||
this.escapeCharPositions = [] | ||
} | ||
@@ -18,6 +19,23 @@ | ||
lunr.QueryLexer.prototype.sliceString = function () { | ||
var subSlices = [], | ||
sliceStart = this.start, | ||
sliceEnd = this.pos | ||
for (var i = 0; i < this.escapeCharPositions.length; i++) { | ||
sliceEnd = this.escapeCharPositions[i] | ||
subSlices.push(this.str.slice(sliceStart, sliceEnd)) | ||
sliceStart = sliceEnd + 1 | ||
} | ||
subSlices.push(this.str.slice(sliceStart, this.pos)) | ||
this.escapeCharPositions.length = 0 | ||
return subSlices.join('') | ||
} | ||
lunr.QueryLexer.prototype.emit = function (type) { | ||
this.lexemes.push({ | ||
type: type, | ||
str: this.str.slice(this.start, this.pos), | ||
str: this.sliceString(), | ||
start: this.start, | ||
@@ -30,4 +48,9 @@ end: this.pos | ||
lunr.QueryLexer.prototype.escapeCharacter = function () { | ||
this.escapeCharPositions.push(this.pos - 1) | ||
this.pos += 1 | ||
} | ||
lunr.QueryLexer.prototype.next = function () { | ||
if (this.pos == this.length) { | ||
if (this.pos >= this.length) { | ||
return lunr.QueryLexer.EOS | ||
@@ -141,2 +164,8 @@ } | ||
// Escape character is '\' | ||
if (char.charCodeAt(0) == 92) { | ||
lexer.escapeCharacter() | ||
continue | ||
} | ||
if (char == ":") { | ||
@@ -143,0 +172,0 @@ return lunr.QueryLexer.lexField |
{ | ||
"name": "lunr", | ||
"description": "Simple full-text search in your browser.", | ||
"version": "2.1.0-alpha.1", | ||
"version": "2.1.0-alpha.2", | ||
"author": "Oliver Nightingale", | ||
@@ -6,0 +6,0 @@ "keywords": ["search"], |
@@ -42,2 +42,34 @@ suite('lunr.QueryLexer', function () { | ||
suite('term escape char', function () { | ||
setup(function () { | ||
this.lexer = lex("foo\\:bar") | ||
}) | ||
test('produces 1 lexeme', function () { | ||
assert.lengthOf(this.lexer.lexemes, 1) | ||
}) | ||
suite('lexeme', function () { | ||
setup(function () { | ||
this.lexeme = this.lexer.lexemes[0] | ||
}) | ||
test('#type', function () { | ||
assert.equal(lunr.QueryLexer.TERM, this.lexeme.type) | ||
}) | ||
test('#str', function () { | ||
assert.equal('foo:bar', this.lexeme.str) | ||
}) | ||
test('#start', function () { | ||
assert.equal(0, this.lexeme.start) | ||
}) | ||
test('#end', function () { | ||
assert.equal(8, this.lexeme.end) | ||
}) | ||
}) | ||
}) | ||
suite('multiple terms', function () { | ||
@@ -164,2 +196,40 @@ setup(function () { | ||
suite('term with field with escape char', function () { | ||
setup(function () { | ||
this.lexer = lex("ti\\:tle:foo") | ||
}) | ||
test('produces 1 lexeme', function () { | ||
assert.lengthOf(this.lexer.lexemes, 2) | ||
}) | ||
suite('lexeme', function () { | ||
setup(function () { | ||
this.fieldLexeme = this.lexer.lexemes[0] | ||
this.termLexeme = this.lexer.lexemes[1] | ||
}) | ||
test('#type', function () { | ||
assert.equal(lunr.QueryLexer.FIELD, this.fieldLexeme.type) | ||
assert.equal(lunr.QueryLexer.TERM, this.termLexeme.type) | ||
}) | ||
test('#str', function () { | ||
assert.equal('ti:tle', this.fieldLexeme.str) | ||
assert.equal('foo', this.termLexeme.str) | ||
}) | ||
test('#start', function () { | ||
assert.equal(0, this.fieldLexeme.start) | ||
assert.equal(8, this.termLexeme.start) | ||
}) | ||
test('#end', function () { | ||
assert.equal(7, this.fieldLexeme.end) | ||
assert.equal(11, this.termLexeme.end) | ||
}) | ||
}) | ||
}) | ||
suite('term with edit distance', function () { | ||
@@ -166,0 +236,0 @@ setup(function () { |
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
589360
78
18507