如何通过 Typeahead 和 Bloodhound 一次性使用多个数据集?

nik*_*ovn 2 javascript json typeahead.js bloodhound

我正在尝试使用 Typeahead/Bloodhound 进行建议和搜索。为简单起见,让我们假设我有两种类型的模型对象 - CountryCity

public class Country
{
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

(服务器端在 ASP.NET 中,但这与问题无关)。

City 实际上与 Country 相同,只是名称不同。

无论如何,在造型之前,我希望最终结果如下所示:

猎犬多个数据集

(如果不明显,我在文本框中写了“AL”,其余字母构成第一个建议)

我可以通过使用多个 Bloodhounds 轻松实现这一点:

var countries = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 5,
    remote: {
        url: 'http://localhost:5000/api/countries/%QUERY',
        wildcard: '%QUERY'
    }
});

var cities = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 5,
    remote: {
        url: 'http://localhost:5000/api/cities/%QUERY',
        wildcard: '%QUERY'
    }
});
Run Code Online (Sandbox Code Playgroud)

和多个输入对象预先输入:

$('#remote .typeahead').typeahead(null,
    {
        name: 'countries',
        display: 'name',
        source: countries,
        templates: {
            empty: [
                '<div class="empty-message">',
                'unable to find any countries that match the current query',
                '</div>'
            ].join('\n'),
            suggestion: Handlebars.compile('<div><strong>{{name}}</strong></div>'),
            header: '<h2>Countries</h2>'
        }

    },
    {
        name: 'cities',
        display: 'name',
        source: cities,
        templates: {
            empty: [
                '<div class="empty-message">',
                'unable to find any cities that match the current query',
                '</div>'
            ].join('\n'),
            suggestion: Handlebars.compile('<div><strong>{{name}}</strong></div>'),
            header: '<h2>Cities</h2>'
        }

    });
Run Code Online (Sandbox Code Playgroud)

但是,在我的真实场景中,我有大约 10 个数据集。创建 10 个单独的查询,结合 JSON 序列化/反序列化可能会当场杀死我的服务器,尤其是对于多个用户。

我更喜欢有一个复合 DTO:

public class CompositeSearchResult
{
    public List<Country> Countries { get; set; }
    public List<City> Cities { get; set; }
    //... and many others
}
Run Code Online (Sandbox Code Playgroud)

...同时以某种方式使用 Bloodhound 处理客户端上的复杂对象。这可能吗?

nik*_*ovn 8

我做到了!

首先我发现,Bloodhound 缓存是智能的——所以如果它已经进行了搜索查询,它将不再使用网络——而是会在缓存中查找。

所以这意味着如果两个数据集的 URL 相同,则查询只在服务器上执行一次,然后结果集会被缓存并被所有的猎犬使用。

回到简单的国家+城市示例,它应该如下所示:

var countries = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 5,
    remote: {
        url: 'http://localhost:5000/api/compositesearch/%QUERY',
        wildcard: '%QUERY',
        transform: function(d) {
            return d.countries;
        }
    }
});

var cities = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 5,
    remote: {
        url: 'http://localhost:5000/api/compositesearch/%QUERY',
        wildcard: '%QUERY',
        transform: function(d) {
            return d.cities;
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

两种情况的唯一区别是远程设置中的“转换”功能。

然后,通过使用问题中完全相同的 CompositeSearch 对象,我可以一次性检索两个数据集(基本上与屏幕截图中的相同)而不是 2 - 由浏览器的 Network 选项卡确认:)