4

what i am looking for is to display suggestion not just with what the word starts but if it meets any part of the word.

for example if i have a list of word [ Manimal, animal, person, erson]

when i type animal or ani it should display both Manimal and animal or if i type son it should display both person and son.

how can i do this with typeahead and bloodhound?

I have tried

var jobCodes = new Bloodhound({
  datumTokenizer: function (d) { return [d.JobName]; },
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  prefetch: "/api/jobservice/getjobcodes"
});

Or 

var jobCodes = new Bloodhound({
  datumTokenizer: function (d) { return Bloodhound.tokenizers.nonword(d.JobName); },
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  prefetch: "/api/jobservice/getjobcodes"

});
Dhaulagiri
  • 3,281
  • 24
  • 26
Justin Homes
  • 3,739
  • 9
  • 49
  • 78
  • I'd do this by hashing digrams or trigrams from the substitution set, and do a set intersection with your typeahead. I don't know anything about Bloodhound, though -- it may very well not support this feature. – Jon Watte Feb 25 '14 at 00:38
  • http://stackoverflow.com/questions/11773013/autocomplete-not-firing/11773174#11773174 may be this will help you... – Ram Singh Feb 25 '14 at 15:06

2 Answers2

0

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.

Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
-1

Create/get the array of strings you want to check against, then define a function which takes the array of strings as the first argument, and the substring as the second argument.

Then create an empty array which will hold the results if there are any.

Loop through each of the strings contained in the array and if any of them contain the substring, push that string into the results array.

Last, if there are results, return them. Otherwise return "no results".

var phrases = ["lazy", "little", "laughing", "ladies"];
var checkStringsForSubString = function(str, subStr) {
    var results = [];
    for (var i = 0; i < phrases.length; i++) {
        if (str[i].indexOf(subStr) != -1) {
            results.push(str[i]);
        }
    }
    if (results.length > 0) {
        return results;
    } else {
        return "no results";
    }
};
checkStringsForSubString(phrases, "la");
mmm
  • 2,272
  • 3
  • 25
  • 41
  • This isn't an answer about typeahead.js, it's just a CS101 answer about coding in general – Chris Moschini Jul 27 '23 at 00:35
  • @ChrisMoschini yes, 15 lines of code instead of pulling in 2 libraries and then still needing an equivalent amount of custom code is obviously the way it should be done. I agree, this is just a CS101 answer that provides a better solution in general. lol – mmm Aug 01 '23 at 20:55
  • Your answer doesn't provide a typeahead, and it would be much larger than 15 lines if it did; further, it doesn't handle the performance challenges of responding to keystrokes with a fast lookup, or the necessary throttling to avoid searching back to back and freezing the browser. Typeahead does all those things - thus the need for a library. The question was about Typeahead. Your answer failed to answer it, nor provide an alternative typeahead. – Chris Moschini Aug 03 '23 at 15:55