Update: You can use my fork of Bloodhound, which offers this as a simple bool option, shouldStartAnyChar
. You may want to combine it with another new flag, shouldMatchAnyToken
:
https://github.com/brass9/typeahead.js
You can see them in JsDoc here in the source:
https://github.com/brass9/typeahead.js/blob/master/src/bloodhound/search_index.js
The above is drop-in compatible with the version from 10 years ago, but, moves portions of the code up a decade, especially eliminating MSIE fallbacks.
If you just want to use the original Typeahead from 10 years ago, read on:
That depends on how eager you are to use Bloodhound, and how much of it you're willing to rewrite.
The simplest answer is to basically turnoff 95% of Bloodhound by using remote:
const bloodhound = new Bloodhound({
remote: {
url: '/searchjson?q=query',
wildcard: 'query'
}
Then on the backend just implement search the way you wanted it, searching anywhere in the phrase not just the start. It will be slower, especially on slow networks like cellphones.
If you must have Bloodhound do its primary job, you are in for an adventure. The way Bloodhound works behind the scenes is it uses SearchIndex, which looks like this:
https://github.com/twitter/typeahead.js/blob/master/src/bloodhound/search_index.js
The 2 parts in use by Bloodhound are the add function, which adds an array of data to its index:
add: function (data) {
var that = this;
data = $_.isArray(data) ? data : [data];
$_.each(data, function (datum) {
var id, tokens;
that.datums[id = that.identify(datum)] = datum;
tokens = normalizeTokens(that.datumTokenizer(datum));
$_.each(tokens, function (token) {
var node, chars, ch;
node = that.trie;
chars = token.split("");
while (ch = chars.shift()) {
node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
node[IDS].push(id);
}
});
});
},
And search, which searches whatever's in there:
search: function search(query) {
var that = this, tokens, matches;
tokens = normalizeTokens(this.queryTokenizer(query));
$_.each(tokens, function (token) {
var node, chars, ch, ids;
if (matches && matches.length === 0) {
return false;
}
node = that.trie;
chars = token.split("");
while (node && (ch = chars.shift())) {
node = node[CHILDREN][ch];
}
if (node && chars.length === 0) {
ids = node[IDS].slice(0);
matches = matches ? getIntersection(matches, ids) : ids;
} else {
matches = [];
return false;
}
});
return matches ? $_.map(unique(matches), function (id) {
return that.datums[id];
}) : [];
},
As you may have noticed, this is both pretty ingenius and pretty complicated. It's a very, very basic database implementation entirely in Javascript, in just 43 lines of code. It's pretty impressive, and a big reason to use Bloodhound...
...but also your main challenge here. The SearchIndex builds a node tree that looks like:
Node = {
i: [1, 5, 6, ...]
c: ['a': Node, 'e': Node, ... ]
}
Where i is the list of Ids that match if you've gotten this deep in the tree, and c is the tree of possible remaining matching characters in the index.
You would need to override the search
method by slowing it down a bit, having it check throughout every token, not just the beginning.
But you'd have an added problem, that overriding just that one method has no intentional way of adding that customization. I think you could acknowledge this code has been stable for a decade and feel safe doing "Monkey-Patching," where you let an object initialize itself then replace as little as possible with your own code before it can act on it.
After initializing a Bloodhound instance:
const bloodhound = new Bloodhound({ ... });
In the source it sets .index
to a SearchIndex instance. You could apply a Monkey-Patch like:
bloodhound.index.search = query => { ...
You'd be in for quite the challenge though, because SearchIndex hides several private methods like getIntersection and normalizeTokens in its class closure you'd have to copy-pasta out to your code.