Have you ever wanted to build a search feature into an application? In the old days, you might have found yourself wrangling with Solr, or building your own search service on top of Lucene — if you were lucky. But, since 2010, there's been an easier way: Elasticsearch. Elasticsearch is an open-source storage engine built on Lucene. It's more than a search engine; it's a true document store, albeit one emphasizing search performance over consistency or durability. This means that, for many applications, you can use Elasticsearch as your entire backend. Applications such as...
Building a Recipe Search Engine
In this article, you'll learn how to use Elasticsearch with AngularJS to create a search engine for recipes, just like the one at OpenRecipeSearch.com. Why recipes?- OpenRecipes exists, which makes our job a lot easier.
- Why not?
cd
to the directory you just unzipped, and run bin/elasticsearch
(bin/elasticsearch.bat
on Windows). Ta-da! You've just started your very own elasticsearch instance. Leave that running while you follow along.
One of the great features of Elasticsearch is its out-of-the-box RESTful backend, which makes it easy to interact with from many environments. We'll be using the JavaScript driver, but you could use whichever one you like; the code is going to look very similar either way. If you like, you can refer to this handy reference (disclaimer: written by me).
Now, you'll need a copy of the OpenRecipes database. It's just a big file full of JSON documents, so it's straightfoward to write a quick Node.js script to get them in there. You'll need to get the JavaScript Elasticsearch library for this, so run npm install elasticsearch
. Then, create a file named load_recipes.js
, and add the following code.
[js]
var fs = require('fs');
var es = require('elasticsearch');
var client = new es.Client({
host: 'localhost:9200'
});
fs.readFile('recipeitems-latest.json', {encoding: 'utf-8'}, function(err, data) {
if (err) { throw err; }
// Build up a giant bulk request for elasticsearch.
bulk_request = data.split('\n').reduce(function(bulk_request, line) {
var obj, recipe;
try {
obj = JSON.parse(line);
} catch(e) {
console.log('Done reading');
return bulk_request;
}
// Rework the data slightly
recipe = {
id: obj._id.$oid, // Was originally a mongodb entry
name: obj.name,
source: obj.source,
url: obj.url,
recipeYield: obj.recipeYield,
ingredients: obj.ingredients.split('\n'),
prepTime: obj.prepTime,
cookTime: obj.cookTime,
datePublished: obj.datePublished,
description: obj.description
};
bulk_request.push({index: {_index: 'recipes', _type: 'recipe', _id: recipe.id}});
bulk_request.push(recipe);
return bulk_request;
}, []);
// A little voodoo to simulate synchronous insert
var busy = false;
var callback = function(err, resp) {
if (err) { console.log(err); }
busy = false;
};
// Recursively whittle away at bulk_request, 1000 at a time.
var perhaps_insert = function(){
if (!busy) {
busy = true;
client.bulk({
body: bulk_request.slice(0, 1000)
}, callback);
bulk_request = bulk_request.slice(1000);
console.log(bulk_request.length);
}
if (bulk_request.length > 0) {
setTimeout(perhaps_insert, 10);
} else {
console.log('Inserted all records.');
}
};
perhaps_insert();
});
[/js]
Next, run the script using the command node load_recipes.js
. 160,000 records later, we have a full database of recipes ready to go. Check it out with curl
if you have it handy:
[code]
$ curl -XPOST http://localhost:9200/recipes/recipe/_search -d '{"query": {"match": {"_all": "cake"}}}'
[/code]
Now, you might be OK using curl
to search for recipes, but if the world is going to love your recipe search, you'll need to...Continue reading %Building a Recipe Search Site with Angular and Elasticsearch%