<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4333017031109575176</id><updated>2012-02-16T20:14:11.904Z</updated><category term='ruby'/><category term='hierarchical faceting'/><category term='solr'/><category term='tools'/><category term='java'/><category term='erlang'/><category term='elasticsearch'/><category term='monitoring'/><category term='cloud'/><category term='api'/><category term='lion'/><category term='book'/><category term='osx'/><category term='mongodb'/><category term='solrcloud'/><category term='hadoop'/><category term='faceted navigation'/><category term='classification'/><category term='grape'/><category term='rails'/><category term='mac'/><category term='search'/><category term='tokenize'/><category term='eclipse'/><category term='jps'/><category term='macports'/><category term='aws'/><category term='token filter'/><category term='facets'/><title type='text'>Springy Web</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.springyweb.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jon Evans</name><uri>http://www.blogger.com/profile/16047943804369012815</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-Z31Q026UJso/TtYlHj809oI/AAAAAAAAAAk/0P0x7f7pEHQ/s220/Jon.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>8</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-6119046507088248557</id><published>2012-02-01T10:30:00.001Z</published><updated>2012-02-01T10:30:57.897Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><title type='text'>Tip: Content assist for static imports in Eclipse</title><content type='html'>&lt;p&gt;You can add a list of "Favourites" in your Eclipse settings to help with content assist for standard static methods. For example, &lt;code&gt;assertNotNull()&lt;/code&gt; and friends when writing unit tests.&lt;/p&gt;&lt;p&gt;To enable this, in your Eclipse Preferences window, navigate to Java / Editor / Content Assist / Favorites. I've got the following types in mine:&lt;ul&gt;	&lt;li&gt;org.hamcrest.Matchers&lt;/li&gt;	&lt;li&gt;org.junit.Assert&lt;/li&gt;	&lt;li&gt;org.springframework.test.web.server.request.MockMvcRequestBuilders&lt;/li&gt;	&lt;li&gt;org.springframework.test.web.server.result.MockMvcResultHandlers&lt;/li&gt;	&lt;li&gt;org.springframework.test.web.server.result.MockMvcResultMatchers&lt;/li&gt;	&lt;li&gt;org.springframework.test.web.server.setup.MockMvcBuilders&lt;/li&gt;&lt;/ul&gt;Add them one at a time with the &lt;em&gt;New Type&amp;hellip;&lt;/em&gt; button.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-6119046507088248557?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/6119046507088248557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2012/02/tip-content-assist-for-static-imports.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/6119046507088248557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/6119046507088248557'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2012/02/tip-content-assist-for-static-imports.html' title='Tip: Content assist for static imports in Eclipse'/><author><name>Jon Evans</name><uri>http://www.blogger.com/profile/16047943804369012815</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-Z31Q026UJso/TtYlHj809oI/AAAAAAAAAAk/0P0x7f7pEHQ/s220/Jon.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-2271978380816320280</id><published>2012-01-24T09:52:00.001Z</published><updated>2012-01-24T14:31:14.906Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='facets'/><category scheme='http://www.blogger.com/atom/ns#' term='faceted navigation'/><category scheme='http://www.blogger.com/atom/ns#' term='hierarchical faceting'/><category scheme='http://www.blogger.com/atom/ns#' term='token filter'/><category scheme='http://www.blogger.com/atom/ns#' term='search'/><category scheme='http://www.blogger.com/atom/ns#' term='elasticsearch'/><category scheme='http://www.blogger.com/atom/ns#' term='classification'/><category scheme='http://www.blogger.com/atom/ns#' term='tokenize'/><title type='text'>Hierarchical Faceting With Elastic Search</title><content type='html'>Our current project is using &lt;a href="http://www.elasticsearch.org/"&gt;ElasticSearch&lt;/a&gt; for, er search.Hierarchical categorization of content found itself pinned to our sprint planning board this week and so I decided to take a look into what options were available.Our classification is a pretty basic hierarchy dividing and redividing things into groups, where each new group is a sub-species of its parent group.&lt;br /&gt;Essentially, at this stage we have two main requirements:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;1. Hierarchical Search&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;By classifying X as:&lt;br /&gt;&lt;pre class="brush: text"&gt;/a/b/c/d&lt;br /&gt;&lt;/pre&gt;We must be able to search for and find X for the following paths:&lt;br /&gt;&lt;pre class="brush: text"&gt;/a&lt;br /&gt;/a/b&lt;br /&gt;/a/b/c&lt;br /&gt;/a/b/c/d&lt;/pre&gt;(I'm using /a/b/c/d for brevity, but imagine /Computer Peripherals/Hard Drives/External/USB for example).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;2. Drill Down with facets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To allow for searching from the more general to the more specific we want to able to allow children of a given facet to be returned.e.g when we ask for the direct children of /a as facets, the system should return /a/b/.&lt;br /&gt;&lt;br /&gt;A useful starting point came not from the ElasticSearch  documentation but from the &lt;a href="http://wiki.apache.org/solr/HierarchicalFaceting"&gt;Solr wiki&lt;/a&gt;, which is a worthwhile read in my opinion. Much of what follows is explained in fuller detail there.&lt;br /&gt;&lt;br /&gt;A nice solution to requirement 1 is provided by Lucene's &lt;a href="http://lucene.apache.org/java/3_3_0/api/all/org/apache/lucene/analysis/path/PathHierarchyTokenizer.html"&gt;PathHierarchyTokenizer&lt;/a&gt;, which is &lt;a href="http://www.elasticsearch.org/guide/reference/index-modules/analysis/pathhierarchy-tokenizer.html"&gt;exposed by ElasticSearch&lt;/a&gt;.&lt;br /&gt;As described in the docs this tokenizer allows us to take input like:&lt;br /&gt;&lt;pre class="brush: text"&gt;/a/b/c/d&lt;br /&gt;&lt;/pre&gt;and produce the following tokens:&lt;br /&gt;&lt;pre class="brush: text"&gt;/a&lt;br /&gt;/a/b&lt;br /&gt;/a/b/c&lt;br /&gt;/a/b/c/d&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This works pretty well, however by using this tokenizer alone it isn’t particularly easy to constrain the depth of the classification (Requirement 2). Therefore we decided to follow the seemingly hacky but certainly workable idea (See facet.prefix in the &lt;a href="http://wiki.apache.org/solr/HierarchicalFaceting"&gt;Solr article&lt;/a&gt;) of storing depth information along with the path.&lt;br /&gt;&lt;br /&gt;So our indexed category terms will now be stored as:&lt;br /&gt;&lt;pre class="brush: text"&gt;0/a&lt;br /&gt;1/a/b&lt;br /&gt;2/a/b/c&lt;br /&gt;3/a/b/c/d&lt;br /&gt;&lt;/pre&gt;i.e. 1/a/b represents the fact that a/b is 1 level below /a.With this information we can now use a &lt;a href="http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html"&gt;Regex Pattern&lt;/a&gt; to get facets for a particular depth - More on this later in the article.&lt;br /&gt;&lt;span style="font-size: large;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Encoding the depth&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We gave some thought to how we might go about encoding depth along with our categories and opted for writing a custom &lt;a href="http://lucene.apache.org/java/3_0_0/api/all/org/apache/lucene/analysis/TokenFilter.html"&gt;TokenFilter&lt;/a&gt; with input provided from the token stream of the PathHierarchyTokenizer.&lt;br /&gt;&lt;br /&gt;The remainder of this article is intended to illustrate how to write and test a TokenFilter as well as how to deploy and reference it for use with ElasticSearch. The full &lt;a href="https://github.com/springyweb/elasticsearch-customisations"&gt;source code&lt;/a&gt; is available on GitHub.&lt;br /&gt;&lt;br /&gt;We are using Maven 3 for managing this customisation so having created a new Maven (jar) project in my IDE, I setup the required dependencies (Full POM is &lt;a href="https://github.com/springyweb/elasticsearch-customisations/blob/master/pom.xml"&gt;here&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml"&gt;   &lt;br /&gt;    &lt;dependencies&gt;&lt;br /&gt;        &lt;dependency&gt;&lt;br /&gt;            &lt;groupid&gt;org.elasticsearch&lt;/groupid&gt;&lt;br /&gt;            &lt;artifactid&gt;elasticsearch&lt;/artifactid&gt;&lt;br /&gt;            &lt;version&gt;${elasticsearch.version}&lt;/version&gt;&lt;br /&gt;            &lt;scope&gt;compile&lt;/scope&gt;&lt;br /&gt;        &lt;/dependency&gt;&lt;br /&gt;        &lt;br /&gt;        &lt;dependency&gt;&lt;br /&gt;            &lt;groupid&gt;org.apache.lucene&lt;/groupid&gt;&lt;br /&gt;            &lt;artifactid&gt;lucene-test-framework&lt;/artifactid&gt;&lt;br /&gt;            &lt;version&gt;${lucene.version}&lt;/version&gt;&lt;br /&gt;            &lt;scope&gt;test&lt;/scope&gt;&lt;br /&gt;        &lt;/dependency&gt;&lt;br /&gt;        &lt;br /&gt;        &lt;dependency&gt;&lt;br /&gt;            &lt;groupid&gt;junit&lt;/groupid&gt;&lt;br /&gt;            &lt;artifactid&gt;junit&lt;/artifactid&gt;&lt;br /&gt;            &lt;version&gt;${junit.version}&lt;/version&gt;&lt;br /&gt;            &lt;scope&gt;test&lt;/scope&gt;&lt;br /&gt;        &lt;/dependency&gt;&lt;br /&gt;    &lt;/dependencies&gt;&lt;br /&gt;&lt;/pre&gt;Note: the lucene-test-framework allows us to easily test our TokenFilter without having to keep deploying to ElasticSearch.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 1 Write a failing test&lt;/b&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public class TokenCountFilterTest {&lt;br /&gt;&lt;br /&gt;    private class TestAnalyzer extends Analyzer {&lt;br /&gt;      @Override&lt;br /&gt;      public TokenStream tokenStream(final String fieldName, final Reader reader) {&lt;br /&gt;        return new TokenCountFilter(new PathHierarchyTokenizer(reader));&lt;br /&gt;      }   &lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    /**&lt;br /&gt;     * Create a string of ${pathElementCount} path elements&lt;br /&gt;     * and assert that each path in the hierarchy is prepended with its depth.&lt;br /&gt;     * &lt;br /&gt;     * e.g given the input /a/a/a/a&lt;br /&gt;     * &lt;br /&gt;     * We would expect the output&lt;br /&gt;     * &lt;br /&gt;     * 0/a&lt;br /&gt;     * 1/a/a&lt;br /&gt;     * 2/a/a/a&lt;br /&gt;     * 3/a/a/a/a&lt;br /&gt;     * @throws IOException &lt;br /&gt;     * &lt;br /&gt;     */&lt;br /&gt;    @Test&lt;br /&gt;    public void testTokens() throws IOException {&lt;br /&gt;      int pathElementCount = 10;&lt;br /&gt;      StringBuffer input = new StringBuffer();&lt;br /&gt;      String[] output = new String[pathElementCount];&lt;br /&gt;      String pathElement = "/a";&lt;br /&gt;      for(int i = 0; i&amp;lt; pathElementCount; i++) {&lt;br /&gt;       input.append(pathElement);&lt;br /&gt;       output[i] = i + input.toString(); &lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      Analyzer testAnalyzer = new TestAnalyzer();&lt;br /&gt;      BaseTokenStreamTestCase.assertAnalyzesTo(testAnalyzer, input.toString(),output);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;On lines 3-8 we create an Analyzer for use with the test.TokenCountFilter is the filter that we are going to write to amend the tokens produced by the instance of PathHierarchyTokenizer which is passed as an argument to the constructor on line 6.&lt;br /&gt;&lt;br /&gt;The javadoc for the &lt;i&gt;testTokens&lt;/i&gt; method describes how the test will work.&amp;nbsp; Note that most of the work is managed by &lt;a href="http://lucene.apache.org/java/3_3_0/api/test-framework/org/apache/lucene/analysis/BaseTokenStreamTestCase.html"&gt;BaseTokenStreamTestCase&lt;/a&gt; on line 38. This Lucene test class provides the static method &lt;i&gt;assertAnalyzesTo, &lt;/i&gt;which accepts an Analyzer to test, an input string to analyze e.g &lt;i&gt;"/a/b/c"&lt;/i&gt; and an array of expected output tokens e.g &lt;i&gt;["0/a", "1/a/b", "2a/b/c"]&lt;/i&gt; which should be emitted after tokenization. Internally BaseTokenStreamTestCase utilizes the JUnit framework to make test assertions based on the input and expected output. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 2 Write the TokenFilter&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Note: This may not be the most efficient way of achieving our goal but performs well enough in our tests. It is important to note that token filters operate on every token produced for a stream. With this in mind one must consider their use carefully. Our category paths will seldom contain more that 5 elements and consequently this filter will not greatly affect performance when used with the PathHierarchyTokenizer.&amp;nbsp; Any future enhancements by us will be posted in a future article and of course we welcome any advice from your own experience.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public class TokenCountFilter extends TokenFilter {&lt;br /&gt;&lt;br /&gt;private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);&lt;br /&gt;private int count = 0;&lt;br /&gt;&lt;br /&gt;public TokenCountFilter(final TokenStream input) {&lt;br /&gt;  super(input);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Prepend the value of ${count} to each token and increment ${count}&lt;br /&gt; */&lt;br /&gt;@Override&lt;br /&gt;public final boolean incrementToken() throws IOException {&lt;br /&gt;  final boolean increment = input.incrementToken();&lt;br /&gt;  if (increment) {&lt;br /&gt;    final char[] count = String.valueOf(this.count++).toCharArray();&lt;br /&gt;    final int newLength = termAtt.length() + count.length;&lt;br /&gt;    final char[] resizedBuffer = termAtt.resizeBuffer(newLength);&lt;br /&gt;    termAtt.setLength(newLength); &lt;br /&gt;    System.arraycopy(resizedBuffer, 0, resizedBuffer, count.length, termAtt.length());&lt;br /&gt;    System.arraycopy(count, 0, resizedBuffer, 0, count.length);&lt;br /&gt;  }&lt;br /&gt;  return increment;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Override &lt;br /&gt;public void end() throws IOException {&lt;br /&gt;  super.end();&lt;br /&gt;  count = 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Override&lt;br /&gt;public void reset() throws IOException {&lt;br /&gt;  super.reset();&lt;br /&gt;  count = 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;For each call to input.incrementToken (line 15) that returns true we end up with a new token in the termAtt buffer (declared on line 3). The remainder of the method manages resizing the termAtt buffer before refilling it with the value of &lt;i&gt;count&lt;/i&gt; followed by the original chars from the buffer hence providing us with the required depth encoded before the term in the index.&lt;br /&gt;&lt;br /&gt;Running our test class will now give us a green bar.&lt;br /&gt;&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;Step 3 Create a factory to provide the TokenFilter to ElasticSearch&lt;/b&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;@AnalysisSettingsRequired&lt;br /&gt;public class TokenCountFilterFactory extends AbstractTokenFilterFactory {&lt;br /&gt;  &lt;br /&gt;  @Inject&lt;br /&gt;  public TokenCountFilterFactory(Index index, @IndexSettings Settings indexSettings,&lt;br /&gt;      @Assisted String name, @Assisted Settings settings) { &lt;br /&gt;      super(index, indexSettings, name, settings);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  @Override&lt;br /&gt;  public TokenStream create(TokenStream tokenStream) {&lt;br /&gt;      return new TokenCountFilter(tokenStream);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This code is fairly self-explanatory and is basically cut and paste code which follows the same pattern as the other factories from ElasticSearch i.e. with a factory method creating a new instance of our TokenCountFilter around the TokenStream.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 4 Make the new code available to ElasticSearch&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Running:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: text"&gt;mvn clean package&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Will compile and the code and generate a jar file. This jar should then be added to $ES_HOME/lib. A restart of ElasticSearch will ensure that our classes get loaded.&amp;nbsp;&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 5 Try it out&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First we can setup a new 'test' index which will use our TokenFilter as part of its analysis:&lt;br /&gt;&lt;pre class="brush: js"&gt;curl -XPUT 'http://localhost:9200/test/' -d '&lt;br /&gt;{&lt;br /&gt;  "index": {},&lt;br /&gt;  "settings": {&lt;br /&gt;    "analysis": {&lt;br /&gt;      "analyzer": {&lt;br /&gt;        "depth_path_hierarchy": { "type": "custom", "tokenizer": "path_hierarchy", "filter": ["token_count"] }&lt;br /&gt;      },&lt;br /&gt;      "filter" : {&lt;br /&gt;  "token_count": {"type": "com.springyweb.elasticsearch.index.analysis.TokenCountFilterFactory"}&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}'&lt;br /&gt;&lt;/pre&gt;Lines 9-11 define our new "token_count" filter. Note that we actually define the type as TokenCountFilterFactory which is the class responsible for creating our TokenFilter.Lines 6-8 define a ""depth_path_hierarchy" analyzer. Note that we specify the "path_hierarchy" tokenizer which will provide the tokens for our "token_count" filter.&lt;br /&gt;&lt;br /&gt;Next we setup a mapping for a type called 'content' which we shall be storing in our 'test' index.&lt;br /&gt;&lt;pre class="brush: js"&gt;curl -XPUT 'http://localhost:9200/test/content/_mapping' -d '&lt;br /&gt;{&lt;br /&gt;     "test_mapping" : {&lt;br /&gt;         "properties" : {&lt;br /&gt;       "categories" : {"type" : "string", "index_analyzer" : "depth_path_hierarchy"}&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt;}'&lt;/pre&gt;A &lt;a href="http://www.elasticsearch.org/guide/reference/mapping/"&gt;Mapping&lt;/a&gt; defines how a document should be mapped to the search engine. In this case we have specified that when &lt;b&gt;indexing&lt;/b&gt; our "categories" property that the &lt;i&gt;"depth_path_hierarchy"&lt;/i&gt; analyzer should be used. Note: For this particular analyzer to be used solely for indexing (i.e. not for search) we used the key &lt;i&gt;"index_analyzer"&lt;/i&gt; in our request. When creating a mapping for a property it is also possible to specify which analyzer is to be used when searching by&amp;nbsp; adding the key/value &lt;i&gt;"search_analyzer" : "&amp;lt;analyzer name&amp;gt;"&lt;/i&gt;, or one can simply use the single key/value&lt;i&gt; "analyzer" : "&amp;lt;analyzer name&amp;gt;" &lt;/i&gt;which will use the same analyzer for indexing and searching the property. Further details regarding mapping can be found &lt;a href="http://www.elasticsearch.org/guide/reference/mapping/root-object-type.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We are now in a position to add some test content to our 'test' index. (Take note of the categories)&lt;br /&gt;&lt;pre class="brush: js"&gt;curl -XPUT http://localhost:9200/test/content/1 -d '{&lt;br /&gt;    "name": "test 1",&lt;br /&gt;  "categories": ["/foo/bar/baz"]&lt;br /&gt;}'&lt;br /&gt;&lt;br /&gt;curl -XPUT http://localhost:9200/test/content/2 -d '{&lt;br /&gt;    "name": "test 2",&lt;br /&gt;  "categories": ["/foo/baz/bar"]&lt;br /&gt;}'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The search shown below verifies that we can find both pieces of content beneath the root "0/foo" category which means that Requirement 1 is satisfied.&lt;br /&gt;Note that we use a &lt;a href="http://www.elasticsearch.org/guide/reference/query-dsl/term-query.html"&gt;term query&lt;/a&gt; which is not analyzed, the reason for this is that we want search for an entire term e.g. /a/b/c/d without that term being tokenized in any way. This makes sense as otherwise a search for /a/b/c/d would be tokenized into path elements (if we use the &lt;i&gt;"depth_path_hierarchy"&lt;/i&gt; analyzer for example) and therefore could find something categorized more generally, /a/b/c for example, which we don't want.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;curl -XGET http://localhost:9200/test/content/_search -d '{&lt;br /&gt;    "query" : {&lt;br /&gt;        "term" : { "categories": "0/foo" }&lt;br /&gt;    }&lt;br /&gt;}'&lt;br /&gt;&lt;br /&gt;returns:&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;  "took" : 1,&lt;br /&gt;  "timed_out" : false,&lt;br /&gt;  "_shards" : {&lt;br /&gt;    "total" : 1,&lt;br /&gt;    "successful" : 1,&lt;br /&gt;    "failed" : 0&lt;br /&gt;  },&lt;br /&gt;  "hits" : {&lt;br /&gt;    "total" : 2,&lt;br /&gt;    "max_score" : 0.5945348,&lt;br /&gt;    "hits" : [ {&lt;br /&gt;      "_index" : "test",&lt;br /&gt;      "_type" : "content",&lt;br /&gt;      "_id" : "1",&lt;br /&gt;      "_score" : 0.5945348, "_source" : {&lt;br /&gt;    "name": "test 1",&lt;br /&gt;"categories": ["/foo/bar/baz"]&lt;br /&gt;}&lt;br /&gt;    }, {&lt;br /&gt;      "_index" : "test",&lt;br /&gt;      "_type" : "content",&lt;br /&gt;      "_id" : "2",&lt;br /&gt;      "_score" : 0.5945348, "_source" : {&lt;br /&gt;    "name": "test 2",&lt;br /&gt;"categories": ["/foo/baz/bar"]&lt;br /&gt;}&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Our final test is to try to fulfill Requirement 2, namely to return direct children as facets based on a given path.&lt;br /&gt;&lt;pre class="brush: js"&gt;curl -XGET http://localhost:9200/test/content/_search?pretty=true -d '{&lt;br /&gt;    "query" : {&lt;br /&gt;            "match_all" : {  }&lt;br /&gt;        },&lt;br /&gt;    "facets" : {&lt;br /&gt;        "category" : { &lt;br /&gt;            "terms" : {&lt;br /&gt;                "field" : "categories",&lt;br /&gt;                "regex" : "^1/foo/.*"&lt;br /&gt;            } &lt;br /&gt;       }&lt;br /&gt;    }&lt;br /&gt;}'&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Note the regex on line 9. Here we are limiting the facets returned to those that start with 1/foo i.e a depth of 1 beneath /foo.The response shows that we get the required facet terms, '1/foo/baz' and '1/foo/bar'&lt;br /&gt;&lt;pre class="brush: js"&gt;"facets" : {&lt;br /&gt;   "category" : {&lt;br /&gt;     "_type" : "terms",&lt;br /&gt;     "missing" : 0,&lt;br /&gt;     "total" : 6,&lt;br /&gt;     "other" : 4,&lt;br /&gt;     "terms" : [ {&lt;br /&gt;       "term" : "1/foo/baz",&lt;br /&gt;       "count" : 1&lt;br /&gt;     }, {&lt;br /&gt;       "term" : "1/foo/bar",&lt;br /&gt;       "count" : 1&lt;br /&gt;     } ]&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;So that wraps this rather long post up. No doubt there are numerous approaches to solving this kind of problem but this article has been intended partially as a solution but more generally as an attemptto illustrate some of the features of ElasticSearch that you may not be familiar with.&lt;br /&gt;&lt;br /&gt;You can download the source code for this article: &lt;a href="https://github.com/springyweb/elasticsearch-customisations"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Happy searching!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-2271978380816320280?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/2271978380816320280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2012/01/hierarchical-faceting-with-elastic.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/2271978380816320280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/2271978380816320280'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2012/01/hierarchical-faceting-with-elastic.html' title='Hierarchical Faceting With Elastic Search'/><author><name>Simon Hutchinson</name><uri>http://www.blogger.com/profile/15567658417141835114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/-jK9-CVpQyc4/TtUaQTOtpII/AAAAAAAAAAQ/cZVxNA19SHI/s220/pipe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-4334681818600588315</id><published>2011-12-13T10:31:00.001Z</published><updated>2011-12-13T10:37:16.486Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='macports'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='lion'/><title type='text'>Error building Erlang on Lion using Macports</title><content type='html'>Macports was hanging during my install of the rabbitmq-server port.Running port -v showed me that an error was occurring during Erlang compilation (RabbitMQ is writtien in Erlang).The issue, it seems to due to the optimisation settings used by llvm-gcc-4.2.The solution is to patch the Erlang Portfile.Edit:&lt;pre class="brush: text"&gt;&lt;br /&gt;/opt/local/var/macports/sources/rsync.macports.org/release/ports/lang/erlang/Portfile&lt;br /&gt;&lt;/pre&gt;Search for:&lt;pre class="brush: text"&gt;&lt;br /&gt;if {${configure.compiler} == "clang"} { &lt;br /&gt;    configure.compiler llvm-gcc-4.2 &lt;br /&gt;} &lt;br /&gt;&lt;/pre&gt;And insert the following directly below it:&lt;pre class="brush: text"&gt;&lt;br /&gt;configure.cflags-delete "-std=c99"&lt;br /&gt;configure.cflags-delete -O2&lt;br /&gt;configure.cxxflags-delete -O2&lt;br /&gt;configure.cflags-append -O0&lt;br /&gt;configure.cxxflags-append -O0&lt;br /&gt;&lt;/pre&gt;Save the file and then re-run the port install.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-4334681818600588315?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/4334681818600588315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2011/12/building-erlang-on-lion.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/4334681818600588315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/4334681818600588315'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2011/12/building-erlang-on-lion.html' title='Error building Erlang on Lion using Macports'/><author><name>Simon Hutchinson</name><uri>http://www.blogger.com/profile/15567658417141835114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/-jK9-CVpQyc4/TtUaQTOtpII/AAAAAAAAAAQ/cZVxNA19SHI/s220/pipe.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-2163522119355616401</id><published>2011-12-08T19:58:00.001Z</published><updated>2012-02-01T10:44:29.143Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mongodb'/><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Useful MongoDB book</title><content type='html'>Kristina Chodorow's &lt;a href="http://shop.oreilly.com/product/0636920019893.do"&gt;'50 Tips and Tricks for MongoDB Developers'&lt;/a&gt; is a concise and really useful resource. It backed up some of my assumptions and taught me a great deal that I had not considered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-2163522119355616401?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/2163522119355616401/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2011/12/useful-mongodb-book.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/2163522119355616401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/2163522119355616401'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2011/12/useful-mongodb-book.html' title='Useful MongoDB book'/><author><name>Simon Hutchinson</name><uri>http://www.blogger.com/profile/15567658417141835114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/-jK9-CVpQyc4/TtUaQTOtpII/AAAAAAAAAAQ/cZVxNA19SHI/s220/pipe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-2013816200471572815</id><published>2011-11-30T13:03:00.001Z</published><updated>2012-02-01T10:44:11.342Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='jps'/><category scheme='http://www.blogger.com/atom/ns#' term='monitoring'/><title type='text'>Java Virtual Machine Process Status Tool</title><content type='html'>I'm sure I'm just way behind the times but I never knew about the &lt;a href="http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jps.html"&gt;Java Virtual Machine Process Status Tool&lt;/a&gt; even though it's been around since Java 1.5.Running jps on the command line with no options, shows me that my Hadoop processes are running.&lt;br /&gt;&lt;pre class="brush: text"&gt;18513 DataNode&lt;br /&gt;18582 SecondaryNameNode&lt;br /&gt;18761 Jps&lt;br /&gt;18699 TaskTracker&lt;br /&gt;18631 JobTracker&lt;br /&gt;&lt;/pre&gt;With the the -l option I can see the package names too&lt;br /&gt;&lt;pre class="brush: text"&gt;18513 org.apache.hadoop.hdfs.server.datanode.DataNode&lt;br /&gt;18763 sun.tools.jps.Jps&lt;br /&gt;18582 org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode&lt;br /&gt;18699 org.apache.hadoop.mapred.TaskTracker&lt;br /&gt;18631 org.apache.hadoop.mapred.JobTracker&lt;br /&gt;&lt;/pre&gt;With -v I can see the the arguments passed to the JVM. &lt;br /&gt;&lt;pre class="brush: text"&gt;18513 DataNode -Dfile.encoding=UTF-8 -Xserver -Dproc_datano...&lt;br /&gt;&lt;br /&gt;18582 SecondaryNameNode -Dfile.encoding=UTF-8 -Dproc_seconda...&lt;br /&gt;&lt;br /&gt;18766 Jps -Dfile.encoding=UTF-8 -Dapplication.home=/System/Li...&lt;br /&gt;&lt;br /&gt;18699 TaskTracker -Dfile.encoding=UTF-8 -Dproc_tasktracker -Xm ...&lt;br /&gt;&lt;br /&gt;18631 JobTracker -Dfile.encoding=UTF-8 -Dproc_jobtracker -Xmx ...&lt;br /&gt;&lt;/pre&gt;A useful tool that is certainly easier than&lt;br /&gt;&lt;pre class="brush: text"&gt; ps aux | grep java&lt;br /&gt;&lt;/pre&gt;Although do note that what looks like the OS process id is actually the local VM identifier which according to jps docs: "is typically, but not necessarily, the operating system's process identifier for the JVM proces"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-2013816200471572815?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/2013816200471572815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2011/11/java-virtual-machine-process-status.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/2013816200471572815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/2013816200471572815'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2011/11/java-virtual-machine-process-status.html' title='Java Virtual Machine Process Status Tool'/><author><name>Simon Hutchinson</name><uri>http://www.blogger.com/profile/15567658417141835114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/-jK9-CVpQyc4/TtUaQTOtpII/AAAAAAAAAAQ/cZVxNA19SHI/s220/pipe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-3602364265077261803</id><published>2011-11-30T12:35:00.000Z</published><updated>2011-12-01T08:14:12.389Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='lion'/><category scheme='http://www.blogger.com/atom/ns#' term='hadoop'/><title type='text'>Hadoop on OSX 10.7</title><content type='html'>When starting Hadoop using start-all.sh I kept seeing the following in my log file&lt;pre class="brush: text"&gt;&lt;br /&gt;"Unable to load realm info from SCDynamicStore"&lt;br /&gt;&lt;/pre&gt;A workaround is suggested on the Hadoop Jira under &lt;a href="https://issues.apache.org/jira/browse/HADOOP-7489"&gt;HADOOP-7489&lt;/a&gt;which is to specify kerberos config on the command line:&lt;pre class="brush: text"&gt;&lt;br /&gt; -Djava.security.krb5.realm=OX.AC.UK -Djava.security.krb5.kdc=kdc0.ox.ac.uk:kdc1.ox.ac.uk&lt;br /&gt;&lt;/pre&gt;This works perfectly but is easier if you add it into the HADOOP_OPTS environment variable in $HADOOP_HOME/conf/hadoop-env.shMine looks like this:&lt;pre class="brush: text"&gt;&lt;br /&gt;export HADOOP_OPTS="-server -Djava.security.krb5.realm=OX.AC.UK -Djava.security.krb5.kdc=kdc0.ox.ac.uk:kdc1.ox.ac.uk"&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-3602364265077261803?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/3602364265077261803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2011/11/hadoop-on-osx-107.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/3602364265077261803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/3602364265077261803'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2011/11/hadoop-on-osx-107.html' title='Hadoop on OSX 10.7'/><author><name>Simon Hutchinson</name><uri>http://www.blogger.com/profile/15567658417141835114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/-jK9-CVpQyc4/TtUaQTOtpII/AAAAAAAAAAQ/cZVxNA19SHI/s220/pipe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-230371926977883968</id><published>2011-11-29T17:46:00.000Z</published><updated>2011-12-01T08:15:14.535Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='solrcloud'/><category scheme='http://www.blogger.com/atom/ns#' term='solr'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='elasticsearch'/><category scheme='http://www.blogger.com/atom/ns#' term='aws'/><title type='text'>ElasticSearch is full of Springy goodness</title><content type='html'>Firstly, I am a huge fan of &lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt;. It is easy to test, blisteringly fast, and incredibly stable. I would recommend it as a solution to many search problems in a heartbeat. However, despite the recent efforts of &lt;a href="http://wiki.apache.org/solr/SolrCloud"&gt;SolrCloud&lt;/a&gt; I have simply found that as a scalable cloud  solution, Solr is still in its infancy.&lt;br /&gt;&lt;br /&gt;Thanks to a &lt;a href="http://pulkitsinghal.blogspot.com/2011/09/multicore-master-slave-replication-in.html"&gt;useful blog article&lt;/a&gt;  I was able to test some multi-core examples out (note: If you do follow the blog example then beware of capitalisation issues with attribute names. - e.g "instancedir" should be camel case - "instanceDir"). My major issue is that the whole approach feels too clunky, particularly for a solution such as ours which requires multi-tennancy and therefore a large number of cores. At the time of writing SolrCloud doesn't seem to have a nice way of discovering other nodes in a cluster. It all works but it isn't slick.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.elasticsearch.org/"&gt;ElasticSearch&lt;/a&gt; on the other hand has been designed for the cloud. James Cook has written a brilliant &lt;a href="http://www.elasticsearch.org/tutorials/2011/08/22/elasticsearch-on-ec2.html"&gt;tutorial&lt;/a&gt; on running ElasticSearch on EC2. In just a few hours I had created some test EC2 instances running ElasticSearch.&lt;br /&gt;Discovery of other ElasticSearch instances was trivial. The cloud-aws plugin allows for several options, e.g security groups or tags. When I stopped the "master" instance, the "slave" instance noticed and took over as master. Asynchronous backup to EC3 just worked.&lt;br /&gt;Multi-tennancy has been made really easy too - If you post to an index that doesn't exist, it gets created. Trivial to get going and so far very impressive.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-230371926977883968?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/230371926977883968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2011/11/elasticsearch-is-full-of-springy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/230371926977883968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/230371926977883968'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2011/11/elasticsearch-is-full-of-springy.html' title='ElasticSearch is full of Springy goodness'/><author><name>Simon Hutchinson</name><uri>http://www.blogger.com/profile/15567658417141835114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/-jK9-CVpQyc4/TtUaQTOtpII/AAAAAAAAAAQ/cZVxNA19SHI/s220/pipe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4333017031109575176.post-8354584985103383631</id><published>2011-11-29T13:32:00.001Z</published><updated>2011-11-29T13:56:00.769Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='grape'/><category scheme='http://www.blogger.com/atom/ns#' term='api'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Developing an API service with Ruby</title><content type='html'>Our current project involves creating a json-based API service, which can be called either from a web browser, or from other code. We started developing it directly in &lt;a href="http://rubyonrails.org/"&gt;Rails&lt;/a&gt;, but then found the &lt;a href="https://github.com/intridea/grape"&gt;grape project&lt;/a&gt; on &lt;a href="https://github.com/"&gt;github&lt;/a&gt;.&lt;br /&gt;Grape seems to be a much better fit for the project than Rails was. We will still use Rails for the front end website, but using grape for the API service means that the code is a lot simpler.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4333017031109575176-8354584985103383631?l=www.springyweb.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.springyweb.com/feeds/8354584985103383631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.springyweb.com/2011/11/developing-api-service-with-ruby.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/8354584985103383631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4333017031109575176/posts/default/8354584985103383631'/><link rel='alternate' type='text/html' href='http://www.springyweb.com/2011/11/developing-api-service-with-ruby.html' title='Developing an API service with Ruby'/><author><name>Jon Evans</name><uri>http://www.blogger.com/profile/16047943804369012815</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-Z31Q026UJso/TtYlHj809oI/AAAAAAAAAAk/0P0x7f7pEHQ/s220/Jon.jpg'/></author><thr:total>0</thr:total><georss:featurename>A5, Paulerspury, Northamptonshire NN12 6, UK</georss:featurename><georss:point>52.103714069999995 -0.936969185</georss:point><georss:box>31.941692569999994 -41.366656685 72.26573557 39.492718315</georss:box></entry></feed>
