Anthony Chu Contact Me

Add Related Posts to a Jekyll Blog Using Azure Search

Sunday, April 5, 2015

Jekyll generates related posts for every blog post; but quite often the results are not relevant. Using the --lsi (latent semantic indexing) flag produces higher quality results but it takes several minutes to generate the index. Since I am already using Azure Search on this blog, I can use its new "more like this" feature to generate related post links instead.

For details on how to set up Azure Search on your blog, check out my previous article: Add Search to a Jekyll Blog for Free with Azure Search.

Azure Search "More Like This"

A new feature in Azure Search is "more like this". It's available in the 2015-02-28-preview version of the API. It's really straightforward to use. Simply call the API with a morelikethis parameter containing a document id (the post id, in this case) and it'll return a list of related documents.

I'm once again using the node-azure-search client to simplify the calls to the Search API. I've wrapped the call in a service that exposes a getRelated() method to get the related links...

function BlogSearchService() {
    var svc = this;

    var indexName = 'blog-posts';
    var client = AzureSearch({
        url: 'https://blog-search.search.windows.net',
        key:'API_KEY_HERE',
        version: '2015-02-28-preview'
    });

    svc.getRelated = function (postId, callback) {
        var searchOptions = { 
            'morelikethis': postId, 
            '$select': 'id, title, link, pubdate'
        };
        client.search(indexName, searchOptions, callback);
    };
}

Displaying the Results

Like on the search page, I'm also using Knockout to bind the related posts results. That is also very straightforward. Here's the JavaScript...

var vm = new RecentPostsViewModel(postId, new BlogSearchService());
ko.applyBindings(vm);


function RecentPostsViewModel(postId, searchService) {
    var vm = this;
    vm.results = ko.observableArray([]);

    searchService.getRelated(postId, function(err, results) {
        if (err) return;

        vm.results(results.slice(0, 5));
    });
}

And here's the view...

<section class="related section" data-bind="visible: results().length">
    <div class="section-inner">
            <h2 class="heading">Related Posts</h2>
            <ul class="list-unstyled jekyll-related" data-bind="foreach: results">
                <li>
                    <div><a data-bind="attr: { href: link }, text: title"></a></div>
                    <div class="date" data-bind="text: moment(pubdate).format('MMMM D, YYYY')"></div>
                </li>
            </ul>
    </div>
</section>

This is currently how the related posts links on this blog, like the ones at the bottom of this post, are generated. Take a look at the source to see the full implementation.