Skip to content

Commit

Permalink
Make the maximum gap between consecutive characters configurable via …
Browse files Browse the repository at this point in the history
…maxGap

Instead of hardcoding a limit of 10, make this user-configurable.
Smaller numbers are more restrictive but make searches much faster.
  • Loading branch information
hansonw committed May 11, 2016
1 parent c2a08f1 commit fc2bf29
Show file tree
Hide file tree
Showing 8 changed files with 27 additions and 5 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export type MatcherOptions = {
// Default: infinite
maxResults?: number,
// Maximum gap to allow between consecutive letters in a match.
// Provide a smaller maxGap to speed up query results.
// Default: unlimited
maxGap?: number;
// Default: 1
numThreads?: number,
Expand Down
5 changes: 5 additions & 0 deletions lib/main.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export type MatcherOptions = {
// Default: infinite
maxResults?: number,

// Maximum gap to allow between consecutive letters in a match.
// Provide a smaller maxGap to speed up query results.
// Default: unlimited
maxGap?: number;

// Default: 1
numThreads?: number,

Expand Down
8 changes: 8 additions & 0 deletions spec/fuzzy-native-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ describe('fuzzy-native', function() {
]);
});

it('respects maxGap', function() {
var result = matcher.match('abc', {maxGap: 1});
expect(values(result)).toEqual([
'abC',
'abcd',
]);
});

it('favours shallow matches', function() {
var result = matcher.match('zzz', {caseSensitive: true});
expect(values(result)).toEqual([
Expand Down
1 change: 1 addition & 0 deletions src/MatcherBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ vector<MatchResult> MatcherBase::findMatches(const std::string &query,
MatchOptions matchOptions;
matchOptions.case_sensitive = options.case_sensitive;
matchOptions.smart_case = false;
matchOptions.max_gap = options.max_gap;

string new_query;
// Ignore all whitespace in the query.
Expand Down
1 change: 1 addition & 0 deletions src/MatcherBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct MatcherOptions {
bool case_sensitive = false;
size_t num_threads = 0;
size_t max_results = 0;
size_t max_gap = 0;
bool record_match_indexes = false;
};

Expand Down
1 change: 1 addition & 0 deletions src/binding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class Matcher : public ObjectWrap {
options.case_sensitive = get_property<bool>(options_obj, "caseSensitive");
options.num_threads = get_property<int>(options_obj, "numThreads");
options.max_results = get_property<int>(options_obj, "maxResults");
options.max_gap = get_property<int>(options_obj, "maxGap");
options.record_match_indexes =
get_property<bool>(options_obj, "recordMatchIndexes");
}
Expand Down
9 changes: 4 additions & 5 deletions src/score_match.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@

using namespace std;

// Maximum allowed distance between two consecutive match characters.
const size_t MAX_DISTANCE = 10;

// Initial multiplier when a gap is used.
const float BASE_DISTANCE_PENALTY = 0.6;

Expand All @@ -37,6 +34,7 @@ struct MatchInfo {
float *memo;
size_t *best_match;
bool smart_case;
size_t max_gap;
};

/**
Expand Down Expand Up @@ -77,8 +75,8 @@ float recursive_match(const MatchInfo &m,
char c = m.needle_case[needle_idx];

size_t lim = m.last_match[needle_idx];
if (needle_idx > 0 && haystack_idx + MAX_DISTANCE < lim) {
lim = haystack_idx + MAX_DISTANCE;
if (needle_idx > 0 && m.max_gap && haystack_idx + m.max_gap < lim) {
lim = haystack_idx + m.max_gap;
}

// This is only used when needle_idx == haystack_idx == 0.
Expand Down Expand Up @@ -158,6 +156,7 @@ float score_match(const char *haystack,
m.haystack_case = options.case_sensitive ? haystack : haystack_lower;
m.needle_case = options.case_sensitive ? needle : needle_lower;
m.smart_case = options.smart_case;
m.max_gap = options.max_gap;

#ifdef _WIN32
int *last_match = (int*)_malloca(m.needle_len * sizeof(int));
Expand Down
2 changes: 2 additions & 0 deletions src/score_match.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#pragma once

#include <cstddef>
#include <vector>

struct MatchOptions {
bool case_sensitive;
bool smart_case;
size_t max_gap;
};

/**
Expand Down

0 comments on commit fc2bf29

Please sign in to comment.