<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Florian Hopf</title>
 <link href="http://blog.florian-hopf.de/atom.xml" rel="self"/>
 <link href="http://blog.florian-hopf.de/"/>
 <updated>2023-08-01T07:53:29+08:00</updated>
 <id>http://blog.florian-hopf.de/</id>
 <author>
   <name>Florian Hopf</name>
   <email>mail@florian-hopf.de</email>
 </author>

 
 <entry>
   <title>Making Your Daily Standups More Effective</title>
   <link href="http://blog.florian-hopf.de/2023/07/making-standups-more-effective.html"/>
   <updated>2023-07-31T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2023/07/making-standups-more-effective</id>
   <content type="html">&lt;p&gt;For a long time I couldn’t really put my finger on what bothered me with how standup meetings are often being run in Scrum like processes. Now that I realized how focusing on the work you should be doing can make them more effective I never want to go back to experiencing them differently.&lt;/p&gt; 

&lt;h2&gt;The Usual Way&lt;/h2&gt;

&lt;p&gt;A very common way to run standups is to go around the room (virtually or in person) and let people answer three questions, often informally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What did I do yesterday?&lt;/li&gt;
&lt;li&gt;What am I planning to do today?&lt;/li&gt;
&lt;li&gt;Are there any blockers?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People would talk about how they spend their time, including the tasks they are working on, sometimes using the board to move tasks around. While this sounds great it has some serious issues and doesn’t lead to the team performing at their best.&lt;/p&gt;

&lt;h2&gt;The Change&lt;/h2&gt;

&lt;p&gt;Instead of asking those questions there’s a lot of benefit to put the board with your tasks on it at the center of the discussion. You start with the tasks on the right side (often in test or in review) and discuss what is needed to move them to done. You then follow all columns until you arrive on the left, making sure that you cover all the work the team is currently committing to do. This is sometimes also called &lt;a href=&quot;https://medium.com/serious-scrum/walking-the-board-on-daily-scrum-5b468c760329&quot;&gt;Walking the board&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Benefits&lt;/h2&gt;

&lt;p&gt;This allows for a few noticeable benefits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don’t miss work items.&lt;/strong&gt; If you only talk about what people did you are not really looking at the work that you should be doing, the work you committed to do for this iteration. This can easily lead to tasks left in review or in progress, without ever being touched or noticed, dragged from sprint to sprint. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You notice how much work is in progress.&lt;/strong&gt; To reduce context switches it’s a good idea to minimize the work items the team or team members keep in progress. If you don’t look at the overall work and only at the work everyone is doing you can easily miss if there are too many open tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You provide an opportunity to do actual team work.&lt;/strong&gt; If you decide that reducing work in progress is something to strive for it’s a good idea to have everyone help finishing work items. This can mean that others are jumping in to help bring tasks to completion, e.g. by testing or pairing on issues. It becomes the responsibility of the team to tackle the tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The meeting can be more engaging.&lt;/strong&gt; Even if you are interested in what others are doing your mind can start wandering when others are taking a few minutes to talk about their schedule. Talking about the tasks will have a higher relevance and also a greater variation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;People don’t need to appear busy.&lt;/strong&gt; For everyone who has been in a role that includes some organizational work or just a broader context of work (like being a tech lead) you will remember times when you had a hard time to actually summarize what you worked on. There’s so many small things, often happening in an unplanned manner and this can lead to you desperately going through your memory or to-do list to find items you did, just so that you have something to say, also including items that might not be relevant for the team.&lt;/p&gt;

&lt;h2&gt;And the Downsides?&lt;/h2&gt;

&lt;p&gt;Even though I hope that everything mentioned so far there can still be some concerns coming up when considering implementing this approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Putting the work in the center of the discussion can feel like making the sync more mechanic&lt;/strong&gt;, less about the people. While this can be true I think this is easily compensated by the reduced stress of people having to be perceived as busy all of the time. Also, you will likely save some of your standup time with this approach - why not use it on something that is actually social? For example, in my current team we spend some time talking about what we did over the weekend every Monday morning, making for an enjoyable start in the week. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You might be missing important content.&lt;/strong&gt; You could say that the fact that the tech lead did two interviews and talked to another team could be relevant for the team. This can again be true, not all work that is relevant for the team will be tracked on the board. But it&amp;#39;s pretty easy to fix this using a single question at the end - “Anything else that is relevant for the team?”. This is the time when people can update everyone on things that are happening that they need to pay attention to. But it’s never the time for people to appear busy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Shoutout to my former colleague &lt;a href=&quot;https://www.linkedin.com/in/alasdair-george-0a13215/&quot;&gt;Alasdair George&lt;/a&gt; for helping me see this problem clearly and put a name on it.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>1-1s For New Managers</title>
   <link href="http://blog.florian-hopf.de/2023/07/1-1s-for-new-managers.html"/>
   <updated>2023-07-03T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2023/07/1-1s-for-new-managers</id>
   <content type="html">&lt;p&gt;When starting out as a manager one of the obvious changes is that you need to pay even more attention to people. One of the core activities to get to know and connect with your reports will be 1-1 meetings. While you will most likely have experienced them already as an individual contributor being on the other end of the table is a bit different. In this post I’ll describe some of my experiences while starting out as a new manager.&lt;/p&gt;

&lt;p&gt;At it’s core 1-1 meetings are a way for reports to bring any concerns or questions to the manager while for the manager they can be a great tool to get to know your people and what drives them. It’s an opportunity to learn how to align the project work with the interest of the people. And it’s also a good way to build a trusting relationship.&lt;/p&gt;

&lt;h2&gt;Logistics&lt;/h2&gt;

&lt;p&gt;There’s lots of thoughts about frequency and many people will advocate for weekly meetings. That’s also how I started, doing weekly 30 minutes but that got too taxing for me, even though I only have 5-7 reports at a time. The time for 1-1s adds to all the other meetings, making me very exhausted, also due to my more introverted nature. I switched to doing 30 minutes biweekly for now with everyone and it seems to work fine so far. Not every person will have the same needs though, in the past I had a report on my team who required a bit more attention, at that time I did one hour a week with them.&lt;/p&gt;

&lt;p&gt;Many people will recommend to not skip 1-1s and I try to adhere to this rule with the exception of me being on leave. Not sure where I got it from (maybe from High output management?) but some people also recommend to not have scheduled recurring meetings. Instead book the next 1-1 at the end of the last one, then you can take vacations or other events into account. &lt;/p&gt;

&lt;h2&gt;Contents&lt;/h2&gt;

&lt;p&gt;As mentioned, a 1-1 should primarily be a time for the report, bringing any topics they would like to discuss. While asking lots of questions from your side is a good idea it should definitively not be a status meeting.&lt;/p&gt;

&lt;p&gt;You should still make sure that some topics are discussed regularly, like career growth or even checking in on recent work experiences. Also, it can happen that reports just don’t bring a lot of topics by themselves - you should still make good use of the time. &lt;/p&gt;

&lt;p&gt;One thing that I am doing is just asking a lot of questions, both on topics provided by engineers but also for areas that I find interesting. It could be something they worked on, something they did, like running a meeting or giving a presentation, something where I either think I can give some feedback or where I suspect some hidden learning. I’ll be drilling down with questions on the area, often leading to some interesting insights. I use this technique most frequently to extract some learnings we can have on the work we do or the process we are following.&lt;/p&gt;

&lt;p&gt;Having an agenda can work but depends on the discipline of the both report and manager. I am currently not doing shared agendas and I also don’t take notes in a shared document. Even though I know this is very useful I didn’t find a good way how to do it yet, mainly because I am taking extremely detailed notes myself during the discussion and those are not really suitable for sharing.&lt;/p&gt;

&lt;p&gt;It is often being mentioned that 1-1s can be a way to give context, to show the purpose of the work. I am rarely doing this. I might not be doing it enough anyway but when I do that I try to share with the whole team, e.g. the impact our work has on the company or if there are things happening that affect us. This aspect of providing context can be useful for new joiners though when I would answer any questions they might have on how the work we are doing impacts the business. &lt;/p&gt;

&lt;p&gt;1-1s can also be used for mentoring but mostly for more junior engineers and only if they ask for it. This could be giving concrete guidance on issues they are facing or sharing your experience on different aspects.&lt;/p&gt;

&lt;p&gt;There are structured ways to run 1-1s as well but I am mostly doing them on the fly. I spend some time in advance though to prepare key questions or topics I want to cover for each person. If you are ever running out of things to cover there are lists with good questions for 1-1s as well (&lt;a href=&quot;https://jasonevanish.com/2014/05/29/101-questions-to-ask-in-1-on-1s/&quot;&gt;example&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;Challenges&lt;/h2&gt;

&lt;p&gt;Awkward silence is an issue I am still struggling with. I am very fast in breaking silence, either by saying something myself or by moving to another topic. Giving the time for people to think can lead to more insights, getting more input from them.&lt;/p&gt;

&lt;p&gt;If people show strong emotions (this happened less frequent for me, but frustration is a more common one) acknowledge them. Don’t try to fix immediately, especially in cases where you can’t solve it. One occurrence that comes to my mind when I experienced this is when a report was eagerly waiting for a promotion and it didn’t happen. &lt;/p&gt;

&lt;p&gt;It’s also important to develop some self awareness of your own emotional state. For example if you are stressed or upset about something else it’s good to talk about it. Otherwise your report might attribute your reaction to themselves. Developing this self awareness will also help you in other areas of your work and life but this is still a constant learning for me. Any books on emotional intelligence can help you learn more.&lt;/p&gt;

&lt;h2&gt;Resources&lt;/h2&gt;

&lt;p&gt;I hope my experiences can be useful for some people starting out. I wouldn’t be surprised if some of the things I am doing now are not forever and I hope I can continue learning on the practices. There’s fortunately quite a bit of information being published on engineering management. Most of the books will touch on 1-1s as well, some that helped me when I started out are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/en/book/show/50363684&quot;&gt;Become an Effective Software Engineering Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://resilient-management.com/&quot;&gt;Resilient management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.juliezhuo.com/book/manager.html&quot;&gt;The Making of a Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jrothman.com/practical-ways-to-lead-and-serve-manage-others-modern-management-made-easy-book-2/&quot;&gt;Practical Ways to Lead and Serve Others&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/en/book/show/33369254&quot;&gt;The Manager’s Path&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, go check out what &lt;a href=&quot;https://uhl-steine-scherben.org/blog/posts/getting_the_most_from_1_on_1s/&quot;&gt;Christian Uhl has written on the topic&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Application Integration with Apache Camel</title>
   <link href="http://blog.florian-hopf.de/2019/07/apache-camel.html"/>
   <updated>2019-07-07T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2019/07/apache-camel</id>
   <content type="html">&lt;p&gt;Apache Camel is a very useful tool when it comes to integrating different systems and technologies. In this post I will introduce some of its concepts and show how you can test and run your application using Spring Boot.&lt;/p&gt;

&lt;h2&gt;Apache Camel&lt;/h2&gt;

&lt;p&gt;Camel is an implementation of many integration patterns, mostly inspired by the book &lt;a href=&quot;http://www.enterpriseintegrationpatterns.com/&quot;&gt;Enterprise Integration Patterns&lt;/a&gt;. Messages are passing through channels from endpoint to endpoint and along the way they can be translated, filtered and routed to other channels/endpoints. This is taken from the website for the book:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/camel-eip.png&quot; alt=&quot;Concepts of Enterprise Integration Patterns&quot;&gt;&lt;/p&gt;

&lt;p&gt;Besides the integration patterns Apache Camel offers implementations of many protocols and technologies in its components. This means not only can you build your application based on the patterns above but there is also the groundwork for doing technical integrations, one example being reading files from an FTP server or downloading data from an email server. &lt;/p&gt;

&lt;p&gt;To use Camel effectively you need to understand a few concepts.&lt;/p&gt;

&lt;h2&gt;Endpoints&lt;/h2&gt;

&lt;p&gt;Endpoints describe how to access external systems. Each endpoint is handled by a component which registers endpoint prefixes during runtime. Some components are available with the core camel dependency, some need to be added explicitly. Examples for components are the MailComponent, for receiving and sending emails, RabbitComponent for use with RabbitMQ and FileComponent, that provides functionality for reading and writing files.&lt;/p&gt;

&lt;p&gt;Endpoints are being configured by URIs. Each component provides a prefix that determines if it&amp;#39;s responsible for an URI. An example URI for the FileComponent:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;file:/opt/storage?move=.success&amp;amp;moveFailed=.error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will poll the directory &lt;code&gt;/opt/storage&lt;/code&gt; for new files. If a file is processed successfully it is moved to the folder &lt;code&gt;.success&lt;/code&gt;, if there is an error, it is moved to &lt;code&gt;.error&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another example for RabbitMQ, that can be used for sending or receiving messages.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;rabbitmq://host:5672/?username=op&amp;amp;password=op
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The documentation of the components normally provides a table with a description of all the parameters that are available.&lt;/p&gt;

&lt;h2&gt;Routes&lt;/h2&gt;

&lt;p&gt;Endpoints are being connected by Routes. Most of the time you will use the nice Java DSL which is available if you extend your class from &lt;code&gt;RouteBuilder&lt;/code&gt;. A simple example that polls files from a folder and writes them to RabbitMQ:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;from(“file:/opt/attachments”).to(“rabbitmq:host:5672/”);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Besides directly connecting endpoints you can also use &lt;code&gt;filter&lt;/code&gt; to skip some messages and &lt;code&gt;choice&lt;/code&gt; to send them to different endpoints. Or you can have any other processing that you wish in between.&lt;/p&gt;

&lt;h2&gt;Exchanges&lt;/h2&gt;

&lt;p&gt;Exchanges are containers for the current message flow, not to be confused with the term Exchange in the AMQP world. It&amp;#39;s a wrapper the contains the in/out message and potential errors. A message consists of a header, that might keep component specific information like the filename for the FileComponent, and a body.&lt;/p&gt;

&lt;h2&gt;Processor&lt;/h2&gt;

&lt;p&gt;A Processor is anything that does work in a route, &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;choice&lt;/code&gt; mentioned above are processors. But you can also do other tasks by implementing the Processor interface, e.g. if you want to do some transformation. &lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;@Override
public void process(Exchange exchange) throws Exception {

} 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This processor can be registered in the route by adding a &lt;code&gt;.process(new MyProcessor())&lt;/code&gt;.  &lt;/p&gt;

&lt;h2&gt;Testing&lt;/h2&gt;

&lt;p&gt;Camel provides support for testing routes in isolation by replacing endpoints with mock endpoints, injected using &lt;code&gt;AdviceWithRouteBuilder&lt;/code&gt;. Messages can be sent to you endpoints using the &lt;code&gt;MessageProducerTemplate&lt;/code&gt;. You can then assert that a message is arriving at the mock endpoint.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;MockEndpoint resultEndpoint = getMockEndpoint(&quot;mock:result&quot;);
resultEndpoint.setAssertPeriod(1000);
resultEndpoint.setExpectedMessageCount(1);

writeToInputFolder(getClass().getResourceAsStream(&quot;/transaction-report.csv&quot;));
resultEndpoint.assertIsSatisfied();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Testing can be a bit special when it comes to Apache Camel as some of the processing is being done asynchronously.&lt;/p&gt;

&lt;h2&gt;Runtime&lt;/h2&gt;

&lt;p&gt;When using Camel standalone you have to take care to start a &lt;code&gt;CamelContext&lt;/code&gt;, which holds all the configured components and routes. When using Spring Boot you can use the spring-boot-camel-starter that will manage the &lt;code&gt;CamelContext&lt;/code&gt; for you. You just create routes and annotate the methods with &lt;code&gt;@Bean&lt;/code&gt;. By setting the property &lt;code&gt;camel.springboot.main-run-controller=true&lt;/code&gt; the application will stay alive even if you haven&amp;#39;t included something like Spring MVC.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I really like the model of Apache Camel. It provides asynchronous processing and you can decouple different parts of the processing, e.g. by first downloading files to a certain folder and then parse them in another route. Both of those tasks can run in isolation. Besides all the different protocols that are implemented Apache Camel also provides solutions for cross cutting concerns like error handling.&lt;/p&gt;

&lt;p&gt;The declarative approach can make it a bit harder to start for beginners but once you find your way around there is also a lot of potential.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Indonesian Language in Lucene, Solr and Elasticsearch</title>
   <link href="http://blog.florian-hopf.de/2018/03/indonesian-analyzer-lucene-solr-elasticsearch.html"/>
   <updated>2018-03-23T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2018/03/indonesian-analyzer-lucene-solr-elasticsearch</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Indonesian_language&quot;&gt;Indonesian, or Bahasa Indonesia,&lt;/a&gt; is a very approachable language for westerners. It uses latin characters, there's a clear structure, no tenses, no gender or plural forms and it contains many foreign words (as a German I especially enjoy the dutch influenced terms like &lt;em&gt;knalpot&lt;/em&gt; for &lt;em&gt;exhaust pipe&lt;/em&gt;). If you're growing up outside of Asia Indonesia might be a quite distant country for you which you don't hear a lot about. But because the country is so big there are actually quite a lot of people speaking the language, making it, together with its sibling Bahasa Melayu, &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_languages_by_total_number_of_speakers&quot;&gt;one of the most common languages on earth&lt;/a&gt;. And if that is not enough, once you visit Indonesia you will see that the people are very positive minded and happy. Maybe another reason to be interested in the language.&lt;/p&gt; 

&lt;p&gt;&lt;img src=&quot;/files/damn-i-love-indonesia.png&quot; alt=&quot;sticker on my laptop&quot;&gt;&lt;/p&gt;

&lt;p&gt;As I&amp;#39;ve been learning a bit of Indonesian and got to spend quite some time in Indonesia for work and leisure I thought it might be a good idea to look into the Indonesian Analyzer for Lucene and see how it processes text. If you don&amp;#39;t know what an Analyzer does I can point you to one of my older posts on &lt;a href=&quot;http://blog.florian-hopf.de/2014/04/the-absolute-basics-of-indexing-data.html&quot;&gt;the absolute basics of indexing data&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;The IndonesianAnalyzer in Lucene&lt;/h2&gt;

&lt;p&gt;If you want to use the IndonesianAnalyzer, it is available with lucene-analyzers-common, which you most likely have included already. You can just create an instance and use it in any way you like. This snippet will display the terms for the text in a String.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;analyze&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terms&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Analyzer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;analyzer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IndonesianAnalyzer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TokenStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;analyzer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tokenStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tokenStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokenStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;incrementToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;terms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokenStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharTermAttribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;The IndonesianAnalyzer in elasticsearch&lt;/h2&gt;

&lt;p&gt;The IndonesianAnalyzer can be used with elasticsearch as well. In the mapping you can refer to it by the analyzer name &lt;code&gt;indonesian&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;mappings&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;doc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;analyzer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;indonesian&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;               
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The elasticsearch documentation also &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html#indonesian-analyzer&quot;&gt;has a section on the analyzer&lt;/a&gt; explaining how to rebuild it using different filters.&lt;/p&gt;

&lt;h2&gt;The IndonesianAnalyzer in Solr&lt;/h2&gt;

&lt;p&gt;Most of the time you would create your own analyzer chain in Solr. This is from the reference guide.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;analyzer&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;tokenizer&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;solr.StandardTokenizerFactory&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;filter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;solr.LowerCaseFilterFactory&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;filter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;solr.IndonesianStemFilterFactory&quot;&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;stemDerivational=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/analyzer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Features of the Analyzer&lt;/h2&gt;

&lt;p&gt;Let&amp;#39;s look at a very simple example sentence first.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Saya mau makan mie ayam.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I want to eat Chicken noodles. Not only did you learn that I like indonesian food but you can also see that the indonesian language uses latin characters and separates words by whitespace. Let&amp;#39;s see what the IndonesianAnalyzer does with this text.&lt;/p&gt;

&lt;p&gt;If you look at the terms produced by the Lucene example above you will get the following list.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;[makan, mie, ayam]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So only three of the five words are left. &lt;em&gt;Saya&lt;/em&gt; (I) and &lt;em&gt;mau&lt;/em&gt; (want to) are dropped. This is caused by a default list of stopwords, words that are considered not to be important when searching. Those words are maintained in a text file that is &lt;a href=&quot;https://github.com/apache/lucene-solr/blob/master/lucene/analysis/common/src/resources/org/apache/lucene/analysis/id/stopwords.txt&quot;&gt;shipped with the analyzer&lt;/a&gt;. If you want to use a different list for you content you can use one of the constructors that accepts a &lt;code&gt;CharArraySet&lt;/code&gt;, for elasticsearch and Solr you can use a custom StopFilter.&lt;/p&gt;

&lt;p&gt;Now, the rest of the words remained the same, there&amp;#39;s no stemming involved yet, which is a common way to process natural language by reducing terms to its base form. Let&amp;#39;s look at another example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Kami, bangsa Indonesia, dengan ini menjatakan kemerdekaan Indonesia.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the first sentence of the declaration of independence of Indonesia which was proclaimed in 1945. We, the people of Indonesia, hereby declare the independence of Indonesia.&lt;/p&gt;

&lt;p&gt;If you process this text using the Analyzer you will get the following list of terms.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;[bangsa, indonesia, jata, merdeka, indonesia]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Again, words like &lt;em&gt;kami&lt;/em&gt;, &lt;em&gt;dengan&lt;/em&gt;, &lt;em&gt;ini&lt;/em&gt; have been removed as those are in the list of stopwords. But something else has happened. &lt;em&gt;menjatakan&lt;/em&gt; became &lt;em&gt;jata&lt;/em&gt; and &lt;em&gt;kemerdekaan&lt;/em&gt; became &lt;em&gt;merdeka&lt;/em&gt;. The Indonesian language doesn&amp;#39;t have verb inflection but there are many prefixes and suffixes that can change the meaning of words. In this case &lt;em&gt;kemerdekaan&lt;/em&gt; (independence) is a variation of &lt;em&gt;merdeka&lt;/em&gt; (independent). There are many prefixes and suffixes. &lt;em&gt;makan&lt;/em&gt; is &lt;em&gt;to eat&lt;/em&gt;, &lt;em&gt;makanan&lt;/em&gt; is &lt;em&gt;food&lt;/em&gt;. &lt;em&gt;minum&lt;/em&gt; is &lt;em&gt;to drink&lt;/em&gt;, &lt;em&gt;minuman&lt;/em&gt; is a &lt;em&gt;drink&lt;/em&gt;. &lt;em&gt;sama&lt;/em&gt; is &lt;em&gt;same&lt;/em&gt;, &lt;em&gt;bersama&lt;/em&gt; is &lt;em&gt;together&lt;/em&gt;. The IndonesianAnalyzer will stem those examples correctly (even though &lt;em&gt;sama&lt;/em&gt; and &lt;em&gt;bersama&lt;/em&gt; are stopwords).&lt;/p&gt;

&lt;h2&gt;Implementation&lt;/h2&gt;

&lt;p&gt;Like most analyzers the IndonesianAnalyzer combines just a few other components, namely a Tokenizer and serveral TokenFilters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;StandardTokenizer&lt;/li&gt;
&lt;li&gt;StandardFilter&lt;/li&gt;
&lt;li&gt;LowercaseFilter&lt;/li&gt;
&lt;li&gt;StopFilter&lt;/li&gt;
&lt;li&gt;SetKeywordMarkerFilter&lt;/li&gt;
&lt;li&gt;IndonesianStemFilter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The IndonesianStemFilter is the interesting component that is responsible for the stemming. It uses the IndonesianStemmer that is based on the paper &lt;a href=&quot;http://www.illc.uva.nl/Publications/ResearchReports/MoL-2003-02.text.pdf&quot;&gt;A Study of Stemming Effects on Information Retrieval in Bahasa Indonesia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As with most other rule based stemmers some words might not be stemmed correctly. An example: &lt;em&gt;menunggu&lt;/em&gt; means &lt;em&gt;waiting&lt;/em&gt;, it is stemmed to &lt;em&gt;unggu&lt;/em&gt;, but the correct base form would be &lt;em&gt;tunggu&lt;/em&gt;. If you want to get rid of cases like this you can either add the words to the &lt;code&gt;stemExclusionSet&lt;/code&gt; that can be passed in to the analyzer to protect them from stemming. Or you can build your own analyzer that uses the StemmerOverrideFilter - maybe that&amp;#39;s material for another blogpost.&lt;/p&gt;

&lt;h2&gt;Scoring&lt;/h2&gt;

&lt;p&gt;Bahasa Indonesia poses an interesting challenge when it comes to scoring search results. Scoring algorithms like TF/IDF and BM25 rely on the frequency of terms. But in Indonesian a plural is often formed by just repeating a word. &lt;em&gt;mobil&lt;/em&gt; means &lt;em&gt;car&lt;/em&gt; - &lt;em&gt;mobil mobil&lt;/em&gt; means &lt;em&gt;cars&lt;/em&gt;. But if a text talks about a single car or multiple cars shouldn&amp;#39;t make a difference when it comes to scoring. Depending on the text you are searching it might be necessary to ignore the frequencies - or write a custom filter that skips words that are repeated immediately.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Stemming doesn&amp;#39;t have a place in every search application. But it&amp;#39;s one of the techniques that can help making natural language more accessible without being too complex. It can make your search seem like magic.&lt;/p&gt;

&lt;p&gt;Working with natural languages is one thing I enjoy a lot when working with search engines. And if like in this case I am learning something about the language in the process that is even better.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Book Review &ndash; Mastering Docker</title>
   <link href="http://blog.florian-hopf.de/2018/03/book-review-mastering-docker.html"/>
   <updated>2018-03-09T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2018/03/book-review-mastering-docker</id>
   <content type="html">&lt;p&gt;Packtpub has not the best reputation when it comes to the quality of books and that is for a reason. But there are some really good books as well, I learned Solr using &lt;a href=&quot;http://blog.florian-hopf.de/2011/02/book-review-solr-14-enterprise-search.html&quot;&gt;Solr 1.4 Enterprise Search Server&lt;/a&gt;, got more familiar with Spring Boot through &lt;a href=&quot;https://www.packtpub.com/application-development/learning-spring-boot&quot;&gt;Learning Spring Boot&lt;/a&gt; and learned some things while reading &lt;a href=&quot;http://blog.florian-hopf.de/2013/06/book-review-hibernate-search-by-example.html&quot;&gt;Hibernate Search by Example&lt;/a&gt; and &lt;a href=&quot;http://blog.florian-hopf.de/2013/02/book-review-gradle-effective.html&quot;&gt;Grade Effective Implementation Guide&lt;/a&gt;. That's why I'm trying their books from time to time again, this time when wanting to read a book on Docker, so I got me &lt;a href=&quot;https://www.packtpub.com/virtualization-and-cloud/mastering-docker-second-edition&quot;&gt;Mastering Docker by Russ McKendrick&lt;/a&gt; during their recent 5$ sale.&lt;/p&gt;

&lt;p&gt;When I picked up the book I had already started working with Docker. I wanted to read a book to get a deeper understandig of the technology, to be able to solve future problems but also because I think learning works best during the time you actually use something.&lt;/p&gt;

&lt;p&gt;My expectations and needs are somehow met, I learned quite some new things while reading it. The author goes through all the commands and tools and explains how to use them step by step using examples. This is a good thing for some (like docker and docker-compose) but might be a bit too much for others (do we really need screenshots of the my profile screen on Dockerhub?). What I was definitively missing is a deeper look at some of the concepts.&lt;/p&gt;

&lt;p&gt;The most interesting parts for me were all the supporting technologies in the ecosystem like Weave and Rancher, including nearly ecstatic reactions (&amp;quot;&lt;a href='https://github.com/hashicorp/consul-template'&gt;consul-template&lt;/a&gt; can do whaaaaaat?&amp;quot;). I can say the book did its job for me. &lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Messaging with RabbitMQ</title>
   <link href="http://blog.florian-hopf.de/2018/03/rabbitmq-messaging-patterns.html"/>
   <updated>2018-03-02T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2018/03/rabbitmq-messaging-patterns</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://www.rabbitmq.com/&quot;&gt;RabbitMQ&lt;/a&gt; is a robust message broker that can be used to implement different messaging patterns. Even though there is an &lt;a href=&quot;https://www.rabbitmq.com/getstarted.html&quot;&gt;exellent tutorial&lt;/a&gt; available (using different languages and frameworks) it can be a bit difficult to get into the concepts. In this post I want to show some different paradigms that can be implemented with RabbitMQ and why I struggled with some of the concepts.&lt;/p&gt;

&lt;h2&gt;Sending and receiving using queues&lt;/h2&gt;

&lt;p&gt;The easiest thing to do is to use a queue for sending the messages and having a consumer that reads from the same queue.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/rabbitmq-messaging-queue.png&quot; alt=&quot;Sending and receiving using a queue&quot; title=&quot;Sending and receiving using a queue&quot;&gt;&lt;/p&gt;

&lt;p&gt;Nothing stops you from having multiple consumers, that each can process messages from the queue. After a message is consumed it is gone from the queue.&lt;/p&gt;

&lt;p&gt;This is especially well suited when having tasks that need to be executed and it doesn&amp;#39;t matter which of the consumer processes the task.&lt;/p&gt;

&lt;h2&gt;Publish/Subscribe&lt;/h2&gt;

&lt;p&gt;Not all use cases are in the way that you just want to consume a message once. Often you want to have multiple consumers that should all process all the messages. One example can be storing objects in different data stores (e.g. a search index and the database), another one domain events like an order that has been submitted and should be processed by the order management system and the inventory system. This calls for a publish/subscribe mechanism and of course RabbitMQ has you covered for this.&lt;/p&gt;

&lt;p&gt;The biggest difference compared to using a queue alone is that in this case the producer doesn&amp;#39;t write to the queue directly anymore. An instance of what is called an Exchange accepts messages and forwards them to one or more queues.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/rabbitmq-messaging-publish-subscribe.png&quot; alt=&quot;Publish/Subscribe&quot; title=&quot;Publish/Subscribe&quot;&gt;&lt;/p&gt;

&lt;p&gt;To have a classic publish/subscribe model you would use a FanoutExchange which forwards the messages to one or more queues. To connect exchange and queue you are declaring a binding, in this case that all messages for a certain exchange should be forwarded to a certain queue. &lt;/p&gt;

&lt;p&gt;Each consumer reads messages from a dedicated queue. That also means that you will need one binding for each consumer that is listening.&lt;/p&gt;

&lt;p&gt;With RabbitMQ it is possible to use queues that are automatically deleted when the consumer stops listening. This allows for very dynamic behaviour with consumers joining and leaving.&lt;/p&gt;

&lt;p&gt;Looking at our first example you might be wondering why there are different ways to send messages, to queues and to exchanges. It turns out that sending to queues really is not possible. There is always a default exchange involved that just forwards the messages. This default exchange just takes the messages for a certain routing key (which is the queue name) and puts them in the queue with the same name. &lt;/p&gt;

&lt;h2&gt;Publish/Subscribe with filtering&lt;/h2&gt;

&lt;p&gt;Besides sending messages to all queues that are registered for an exchange it is also possible to filter them according to a routing key. All messages are being sent to an exchange and this exchange decides by looking at the routing key to which queues a message should be sent to. &lt;/p&gt;

&lt;p&gt;If you want to do this for an exact match of the routing key this is being done by the DirectExchange.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/rabbitmq-messaging-routing.png&quot; alt=&quot;Routing&quot; title=&quot;Routing&quot;&gt;&lt;/p&gt;

&lt;p&gt;When binding a DirectExchange to a queue you need to supply a routing key that will determine which routing keys will be considered for this queue. If you want to assign multiple routing keys you can just add multiple bindings for the same queue. &lt;/p&gt;

&lt;p&gt;You can also supply wildcards for determining which routing keys should be used for a queue, this is done by using a TopicExchange that expects hierarchical routing keys. &lt;/p&gt;

&lt;p&gt;Those two exchanges - Direct and Topic exchange - probably were confusing me the most at the beginning. When it comes to topic I am thinking about classic publish subscribe systems like when using a FanoutExchange. The exchange name is the topic clients are registering for. But here topic refers to a kind of routing on an existing exchange. Same with the DirectExchange: I would have expected a direct exchange to be similar to the first example where you send messages to queues directly. But a direct exchange in this case refers to direct routing and you always need to supply a routing key for this. &lt;/p&gt;

&lt;p&gt;If you want to know more about the different kinds of exchanges head over to the &lt;a href=&quot;https://www.rabbitmq.com/getstarted.html&quot;&gt;tutorials on the RabbitMQ website&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Book Review &ndash; Working Effectively with Legacy Code</title>
   <link href="http://blog.florian-hopf.de/2018/02/book-review-working-effectively-legacy-code.html"/>
   <updated>2018-02-23T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2018/02/book-review-working-effectively-legacy-code</id>
   <content type="html">&lt;p&gt;I am feeling a bit embarrassed that I read &lt;a href=&quot;https://www.goodreads.com/book/show/44919.Working_Effectively_with_Legacy_Code&quot;&gt;Working Effectively with Legacy Code by Michael Feathers&lt;/a&gt; only recently. It has been recommended so many times to me and &lt;a href=&quot;http://www.dev-books.com/&quot;&gt;it's the top mentioned book on Stackoverflow&lt;/a&gt; but somehow I also expected that it doesn't contain any new revelations for me &amp;ndash; which is far from reality.&lt;/p&gt;

&lt;p&gt;You will have heard Michaels definition of legacy code before: Legacy code is code without tests. It&amp;#39;s no surprise that the book focuses a lot on strategies for getting legacy code under test. It consists of many rather small chapters that each focus on a certain aspect of working with legacy code with chapter titles like &lt;i&gt;This Class is Too Big and I Don&amp;#39;t Want It to Get Any Bigger&lt;/i&gt; or &lt;i&gt;I&amp;#39;m Changing the Same Code All Over the Place&lt;/i&gt;. I&amp;#39;ve been reading the book cover to cover but those chapters can also be read standalone, especially when coming back to the book later. The examples are mostly in Java and some in C++ (that poses some unique problems).&lt;/p&gt;

&lt;p&gt;The most valuable aspect of the book is that it provides a very structured way of thinking about legacy code. Not only by providing recipes but also by defining certain terms, the most important one being the &lt;em&gt;seam&lt;/em&gt;, a place in your code that you can use to replace some behaviour in tests. This can be to &lt;em&gt;separate&lt;/em&gt; some unwanted blocks of code (e.g. a remote http call, a database interaction) or to &lt;em&gt;sense&lt;/em&gt; an interaction &amp;ndash; to be able to see that a certain code path is being taken and maybe even look at the variables at this point. To get seams into place you use &lt;em&gt;dependency breaking&lt;/em&gt; techniques. The book contains a catalog of those in the last part, some examples being &lt;i&gt;Extract and Override Call&lt;/i&gt; or &lt;i&gt;Extract Interface&lt;/i&gt; .&lt;/p&gt;

&lt;p&gt;Only while listening to a &lt;a href=&quot;http://www.se-radio.net/2017/06/se-radio-episode-295-michael-feathers-on-legacy-code/&quot;&gt;recent interview with Michael on SE-Radio&lt;/a&gt; I noticed that the book doesn&amp;#39;t mention a technique you will hear often when people talk about legacy code: Implementing a system wide test before doing any changes, e.g. by using something like Selenium or &lt;a href=&quot;http://blog.thecodewhisperer.com/permalink/surviving-legacy-code-with-golden-master-and-sampling&quot;&gt;golden master testing&lt;/a&gt;. Instead Michael focuses on writing tests that are close to the change and will do some refactorings without having tests in place to get the system in a testable state.&lt;/p&gt;

&lt;p&gt;I&amp;#39;d be very interested how a book like this (that I still consider very relevant in its 2004 state) would look like if it was written today with all the more advanced mocking and system test tools available. I&amp;#39;m excited that there is &lt;a href=&quot;https://www.amazon.com/Brutal-Refactoring-Working-Effectively-Legacy/dp/032179320X/&quot;&gt;an announcement for a new book on the same topic by Michael&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;A lot of people tend to shy away from working with legacy code. But even in your greenfield project it won&amp;#39;t take long to see areas that need improvement. So the techniques in this book are definitively relevant for everybody. If you haven&amp;#39;t read the book yet &amp;ndash; go ahead and do it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Service Testing with Docker Containers</title>
   <link href="http://blog.florian-hopf.de/2018/02/service-testing-docker-containers.html"/>
   <updated>2018-02-14T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2018/02/service-testing-docker-containers</id>
   <content type="html">&lt;p&gt;During the recent months I've been helping a company improving their automated testing practices. Besides doing coaching on TDD I also had the chance to work on a project consisting of multiple services where I was able to introduce some service tests using Docker. It's the first time I've used Docker on a project for real and I was quite happy how useful it can be for doing service tests in a distributed environment. In this post I will describe a few of the things I did and learnt along the way.&lt;/p&gt;

&lt;h2&gt;Motivation&lt;/h2&gt;

&lt;p&gt;It&amp;#39;s common wisdom that having tests on the lowest level can be very beneficial, as being symbolized by the &lt;a href=&quot;https://martinfowler.com/bliki/TestPyramid.html&quot;&gt;test pyramid&lt;/a&gt; as well. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit Tests can be executed a lot faster&lt;/li&gt;
&lt;li&gt;it&amp;#39;s easier to identify why a test failed &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But having unit tests often is not enough. In reality failures often hide in the integration of components, be it technical or in the interaction of the components. &lt;/p&gt;

&lt;p&gt;If all your remote calls are mocked you might never notice that you have configured your http client the wrong way. If you never ran your tests on a real database you might never notice that your transaction is never being committed or that the sql you use for migrating the table only works on your in memory database.&lt;/p&gt;

&lt;p&gt;One way to solve this is building end to end tests that drive the application through the frontend and execute real user behaviour on the system. This might sound like a good idea at first but often this leads to a very fragile system. Tests might pass on one day and fail on another.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some network segment might not be available&lt;/li&gt;
&lt;li&gt;Some other application team might have dropped their database on the staging environment &lt;/li&gt;
&lt;li&gt;a million other things can have happened &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But all you can see is that your tests failed and you have to investigate why.  &lt;/p&gt;

&lt;p&gt;You can have far more reliable tests if you only test some components in isolation. Startup a service with all its required dependencies, execute a request on it and see that the result is as expected, be it a response, an entry in a database, a message on a queue or anything else. &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/service-tests.png&quot; alt=&quot;Arrange, Act and Assert&quot; title=&quot;Arrange, Act and Assert&quot;&gt;&lt;/p&gt;

&lt;p&gt;If there are any remote services that are being used by the system you can integrate very simple mock services. After all the intention of those tests is not to test everything that can be tested but only some representative areas. &lt;/p&gt;

&lt;h2&gt;Docker&lt;/h2&gt;

&lt;p&gt;Now, how does &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; help with this? It allows you to easily start your components as separate containers that can interact with each other. A container runs one of the components, there is one container for each database, one container for the service, one container for each mock and so on. &lt;/p&gt;

&lt;p&gt;Containers are started from images that are specified using a Dockerfile. It can extend an existing image (e.g. a container that provides a java runtime) and add application specific tasks (e.g. runtime flags). An example: the following dockerfile is what is being generated by JHipster for a service.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;FROM openjdk:8-jre-alpine

ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
    JHIPSTER_SLEEP=0 \
    JAVA_OPTS=&quot;&quot;

# add directly the war
ADD *.war /app.war

EXPOSE 8081
CMD echo &quot;The application will start in ${JHIPSTER_SLEEP}s...&quot; &amp;amp;&amp;amp; \
    sleep ${JHIPSTER_SLEEP} &amp;amp;&amp;amp; \
    java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.war
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It extends a jre image that provides the Java Runtime, adds the war file with the application code and has the command to start it. This Dockerfile can be used to create an image using &lt;code&gt;docker build&lt;/code&gt; and it can be run using &lt;code&gt;docker run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have multiple containers that should be run together as it is the case with the tests we are executing here you can use docker compose. You specify the services in a yml file, you can pass in environment variables and many other settings. A simple example for a service that uses PostgreSQL. &lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2'&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;my-service&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-service&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SPRING_DATASOURCE_URL=jdbc:postgresql://postgresql:5432/my-service&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SPRING_DATASOURCE_USERNAME=user&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SPRING_DATASOURCE_PASSWORD=&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;postgresql&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres:9.6.2&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_USER=user&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_PASSWORD=&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;5432:5432&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is starting two containers: One for the service &lt;code&gt;my-service&lt;/code&gt; and one for the database. During execution there will be a dedicated network that allows the hosts for the containers to be resolved by their service name, this is why we can use the url &lt;code&gt;jdbc:postgresql://postgresql:5432/my-service&lt;/code&gt; in this example. &lt;/p&gt;

&lt;p&gt;Which environments variables are available of course depend on the image you are using. For PostgreSQL you can see that you can define a user and a password.&lt;/p&gt;

&lt;p&gt;One thing you want to make sure when working with existing images for databases and other components: Always use a fixed version, don&amp;#39;t use &lt;code&gt;latest&lt;/code&gt; or you might be in for some surprises when running on different machines.&lt;/p&gt;

&lt;h2&gt;Writing Tests&lt;/h2&gt;

&lt;p&gt;What kind of tests you write of course heavily depends on the kind of application you are looking at. For many applications it could mean to send a http request to the service and check afterwards if there is a new entry in the database. Of course there can also be many other results to be checked: A file that is being created, a message that is being sent to a queue, another request that is being sent to another service.&lt;/p&gt;

&lt;p&gt;As you don&amp;#39;t need to communicate with your service in process there is also no need to use the same technology and even if you do I don&amp;#39;t think you should share any code. For example for a Java application that uses Hibernate I wouldn&amp;#39;t reuse the entity classes in the tests but use plain JDBC or any other technologies instead. Just implement the parts that you really need to validate the basic functionality.&lt;/p&gt;

&lt;p&gt;A Java library that can be quite useful for writing this kind of tests is &lt;a href=&quot;https://github.com/awaitility/awaitility&quot;&gt;Awaitility&lt;/a&gt;. It implements mechanisms for testing asyncronous interactions, mostly by means of polling. The code for waiting for a condition can look something like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;    &lt;span class=&quot;n&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;atMost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeUnit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;SECONDS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;until&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasNewEntry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;with &lt;code&gt;hasNewEntry()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Callable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hasNewEntry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jdbcTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;queryForObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;select count(*) from my_database_entry&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The easiest way to run the tests is to start a container for them as well, which makes the other services available by their name as hostname in the same network. For Java based tests you can derive your image from the maven image and add your project files to it. When adding the flag &lt;code&gt;--abort-on-container-exit&lt;/code&gt; for the docker-compose run you can make sure that all containers are being shut down when one container ends, which most likely will be the test container.&lt;/p&gt;

&lt;p&gt;You can add the integrationtests to the docker compose file:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;    &lt;span class=&quot;s&quot;&gt;integrationtest&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integrationtest&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./wait-for-it.sh -t 150 my-service:8081 -- mvn test&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$PWD/target/surefire-reports:/app/target/surefire-reports&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/.m2/repository:/root/.m2/repository&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I am using &lt;a href=&quot;https://github.com/vishnubob/wait-for-it&quot;&gt;wait-for-it&lt;/a&gt; that can be used to only execute a command when a certain service is available. In this case we are waiting until something is available at &lt;code&gt;my-service:8081&lt;/code&gt; with a timeout of 150 seconds. &lt;/p&gt;

&lt;p&gt;The test output directory and the local maven repository are added as volumes, which makes them available in the container filesystem. The repository prevents downloading the artifacts again and again, the test output directory is where the reports are being written.&lt;/p&gt;

&lt;p&gt;Once a setup like this is in place people might be keen to write as many tests as possible using this approach, testing different inputs and error conditions. Don&amp;#39;t. Just use it what it is for, testing integration of the different technologies. For the business tests go with the smaller integration or unit tests.&lt;/p&gt;

&lt;p&gt;When there are other services involved you of course need to make sure that those are at least available. Having very simple dummys that return predefined results can get you a long way already. Again, you can use any technology for this. For http base services tools like &lt;a href=&quot;https://expressjs.com/&quot;&gt;Express&lt;/a&gt; or &lt;a href=&quot;https://spring.io/guides/gs/serving-web-content/&quot;&gt;Spring MVC&lt;/a&gt; can be good choices.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It took me a while to get used to the different concepts in Docker and how to combine everything but it can be a really powerful tool to tests services in isolation.&lt;/p&gt;

&lt;p&gt;This approach is a lot better than what I have seen in many companies: Using real services in a dev or staging environment in testing which means that when those go down or have different data than expected your tests will fail. For a more modern approach of testing in production (which I don&amp;#39;t have a lot of experience with) have a look at &lt;a href=&quot;https://medium.com/@copyconstruct/testing-microservices-the-sane-way-9bb31d158c16&quot;&gt;this post by Cindy Sridharan&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Book Review - NoSQL Distilled</title>
   <link href="http://blog.florian-hopf.de/2017/11/book-review-nosql-distilled.html"/>
   <updated>2017-11-29T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2017/11/book-review-nosql-distilled</id>
   <content type="html">&lt;p&gt;Like many others I have a tendency of &lt;a href=&quot;https://en.wikipedia.org/wiki/Tsundoku&quot;&gt;buying more books than I can read&lt;/a&gt;. &lt;a href=&quot;https://martinfowler.com/books/nosql.html&quot;&gt;NoSQL Distilled&lt;/a&gt; by Pramod J. Sadalage and Martin Fowler is one of those books I got lying around for quite some time. I don't regret at all having it picked up now.&lt;/p&gt;

&lt;p&gt;The short book consists of two parts, one that goes in the concepts behind the technologies and one that shows some details of concrete NoSQL databases. You will learn about the different data models, data distribution using replication and sharding, consistency and the CAP theorem. The second part introduces specialities of each type of data store, how to handle schema migrations and how to choose a data store.&lt;/p&gt;

&lt;p&gt;NoSQL distilled builds on the very useful notion of distinguishing aggregate oriented and non aggregate oriented databases. Document databases, columnar and key value stores all are aggregate oriented, in the sense that all of the data for one aggregate is stored together and can be retrieved using a single key. Relational and graph databases are aggregate ignorant because the aggregates in your application will be stored in different tables or nodes and only be combined during read or update.&lt;/p&gt;

&lt;p&gt;The book is short and easy to read. The authors mention that they inteded to have it read during a plane flight. Though I have read some parts of it on a long distance flight I am not too sure I could really manage to consume all of the information in one session :). With a moving target like NoSQL you might think that in over five years since the publication there will be lots of outdated material in the book. But the authors managed to really extract the concepts behind the technology so most of the book is still as relevant as when it was first published. I can recommend it, definitively not only if this is your first contact with NoSQL.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Learning to Build Clojure Webapps</title>
   <link href="http://blog.florian-hopf.de/2017/10/learning-clojure-webapps.html"/>
   <updated>2017-10-20T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2017/10/learning-clojure-webapps</id>
   <content type="html">&lt;p&gt;A while ago I gave a talk at an internal event at Zenika Singapore. We were free to choose a topic so I chose something I thought I didn't know enough about - what it feels like to build a web app in Clojure. This post is a transcript of the talk. I'll go into some details on Clojure, which libraries you can use to build web apps and how all of that felt to me.&lt;/p&gt;

&lt;h2&gt;Clojure&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://clojure.org/&quot;&gt;Clojure&lt;/a&gt; is a LISP dialect that targets the JVM, Microsofts CLR and JavaScript by means of ClojureScript. It is a functional and dynamic language. One of its specialities are the immutable data structures that rely on structural sharing when appending or removing elements. This allows for good performance even though maintaining immutability.&lt;/p&gt;

&lt;p&gt;I got mainly interested in Clojure because&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is very different from all the C based languages around &lt;/li&gt;
&lt;li&gt;while still being a general purpose language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Due to some of its features like Software Transactional Memory (STM) and atoms it can be especially well suited for building correct concurrent applications.&lt;/p&gt;

&lt;p&gt;One aspect that I deem important as well: It seems to be a quite friendly community and I liked both of the user groups I attended, &lt;a href=&quot;https://www.meetup.com/de-DE/Clojure-Berlin/&quot;&gt;Clojure Berlin&lt;/a&gt; and the &lt;a href=&quot;https://www.meetup.com/de-DE/Singapore-Clojure-Meetup/&quot;&gt;Clojure Meetup Singapore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to get started with Clojure it&amp;#39;s best to start with the very common build tool &lt;a href=&quot;https://leiningen.org/&quot;&gt;Leinigen&lt;/a&gt;. Among other things it offers a simple way to build and run projects, standalone or in the REPL, a feature that is commonly used with LISP dialects.&lt;/p&gt;

&lt;p&gt;Using the REPL and Leiningen alone will not be enough for you, you at least need something to edit files. Naturally many people seem to use emacs, besides that there is also &lt;a href=&quot;https://cursive-ide.com/&quot;&gt;Cursive&lt;/a&gt; (built on IntelliJ), &lt;a href=&quot;http://lighttable.com/&quot;&gt;Lighttable&lt;/a&gt; (an experimental IDE) and &lt;a href=&quot;https://sekao.net/nightcode/&quot;&gt;Nightcode&lt;/a&gt; (a very simple editor with built in REPL).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/nightcode.png&quot; alt=&quot;Nightcode&quot;&gt; &lt;/p&gt;

&lt;p&gt;To get started with a project you can just install Leiningen and get started by firing up a REPL using &lt;code&gt;lein repl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let&amp;#39;s start with a simple operation, adding two numbers.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;user=&amp;gt; (+ 2 3)
5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This already shows two of the more unusual features of Clojure. First, all of the code is represented as a list. That is why even the operation is enclosed in brackets. Second, Clojure uses the prefix notation even when it comes to mathematical operations. But this has also the benefit that you can just increase the number of parameters for the add operation.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;user=&amp;gt; (+ 2 3 4 5)
14
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And if you think about it: That is not that unusual at all. If you see + as the name of a function (which it is) this is very similar to the way you would call a function in a c like language.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;plus(2, 3, 4, 5) 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course you can also assign the result of a calculation to a variable.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;user=&amp;gt; (def result (+ 2 3 4 5))
#'user/result
user=&amp;gt; result
14
user=&amp;gt; (- result 1)
13
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Besides numeric values there are also other data types, e.g. strings and boolean values that you can use directly.&lt;/p&gt;

&lt;p&gt;It is very easy to create functions as well.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;user=&amp;gt; (defn append-mod [val] (str val &quot;-mod&quot;))
#'user/append-mod
user=&amp;gt; (append-mod &quot;some-value&quot;)
&quot;some-value-mod&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The more interesting data structures are the collections. There are vectors for sequential data.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;user=&amp;gt; (def characters [&quot;a&quot; &quot;b&quot; &quot;c&quot;])
#'user/characters
user=&amp;gt; (characters 0)
&quot;a&quot;
user=&amp;gt; (characters 1)
&quot;b&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And there are maps, often used with so called keywords as keys.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;user=&amp;gt; (def my-map {:key &quot;value&quot; :foo &quot;bar&quot;})
#'user/my-map
user=&amp;gt; (my-map :key)
&quot;value&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Being a functional language it is very common to do transformation on collections, e.g. using the map operation. This also shows the use of functions as first class citizens as seen by passing in the &lt;code&gt;upper-case&lt;/code&gt; function.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;userer=&amp;gt; (map clojure.string/upper-case characters)
(&quot;A&quot; &quot;B&quot; &quot;C&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Important: this doesn&amp;#39;t modify the existing collection - it creates a new one. All possible due to the efficient implementation of the data structures.&lt;/p&gt;

&lt;h3&gt;Challenges&lt;/h3&gt;

&lt;p&gt;It is true that Clojure has a simple syntax that makes it easy to get started. But there are still many things to learn and when beginning it doesn&amp;#39;t matter that much if something is syntax, a macro or a library call. It can be especially confusing that sometimes there are related but different concepts. For example those are all different ways to create a function, but for different purposes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(defn name [] (body))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(def name (fn [] (body)))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#(body)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing that is still difficult for me is to decide how to structure programs in Clojure. It seems to be common to have a lot of functions in the same namespace. I imagine that it is difficult to decide which function is responsible for which data or task.&lt;/p&gt;

&lt;p&gt;Finally, at least to me, the docs can be confusing.&lt;/p&gt;

&lt;h2&gt;Building Web Apps&lt;/h2&gt;

&lt;p&gt;As a general purpose language Clojure can of course be and is used a lot for building web apps as well. Compared to the Java landscape there is a lot less choice, a very common combination is to use three libraries, &lt;a href=&quot;https://github.com/ring-clojure/ring&quot;&gt;Ring&lt;/a&gt; as the core library, &lt;a href=&quot;https://github.com/weavejester/compojure&quot;&gt;Compojure&lt;/a&gt; for routing and &lt;a href=&quot;https://github.com/weavejester/hiccup&quot;&gt;Hiccup&lt;/a&gt; for templating.&lt;/p&gt;

&lt;p&gt;Ring is implemented as a pipeline. First there is an adapter that is used to map to an internal request/response representation and that adapts to an existing web runtime. There is an adapter that use Jetty or another one that only relies on the Servlet API which makes it possible to deploy a Ring app to any servlet container. &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/ring.png&quot; alt=&quot;Ring flow&quot;&gt; &lt;/p&gt;

&lt;p&gt;Next there are the middlewares. Those are like filters in the Java servlet world and can be used to enhance the application. By default there are some middlewares configured e.g. for handling parameters, session or cookies.&lt;/p&gt;

&lt;p&gt;In the end a request hits the handler which transforms the request to a response.&lt;/p&gt;

&lt;p&gt;Compojure can help you implement the routing for those handlers. This is a simple example that creates one successful route and one function to handle paths that are not found.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;(defroutes app
  (GET &quot;/&quot; [] &quot;&amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;&quot;)
  (route/not-found &quot;&amp;lt;h1&amp;gt;Page not found&amp;lt;/h1&amp;gt;&quot;))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you want to get started yourself the easiest way is to use the &lt;a href=&quot;https://github.com/weavejester/compojure-template&quot;&gt;leiningen-compojure template&lt;/a&gt; that creates the application skeleton for you.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;lein new compojure my_project_name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create the necessary folder structure, a leiningen file, a handler and a test for it.&lt;/p&gt;

&lt;p&gt;If you don&amp;#39;t want to write all the html in Strings yourself like in the example above it&amp;#39;s time for a templating library. One that is often used in combination with Ring and Compojure is Hiccup. It allows you to write Clojure code that is being translated to HTML.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;user=&amp;gt; (html [:span {:class &quot;foo&quot;} &quot;bar&quot;])
&quot;&amp;lt;span class=&quot;\&quot;foo\&quot;&quot;&amp;gt;bar&amp;lt;/span&amp;gt;&quot; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I developed a very simple application using those libraries: a tool for seeing and adding vocabulary. Once started you can see a list of vocabulary, add more words and look up translations. I wouldn&amp;#39;t say it&amp;#39;s production ready for now, one reason being that it stores all the data in memory only :) You can find the &lt;a href=&quot;https://github.com/fhopf/clojure-vocabulary&quot;&gt;source code on Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/clojabulary.png&quot; alt=&quot;The worlds most advanced vocabulary trainer&quot;&gt; &lt;/p&gt;

&lt;p&gt;It contains a GET and a POST route for reading the list and adding a word and a Hiccup template.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It was fun working on a new language and after grasping the basics of Clojure it is easy to get started with a webapp, mainly because of the Leiningen Compojure template that makes it easy to set up a new project. There is a lot of ready made functionality available in the Ring middlewares. Hiccup still feels a bit weird to me, I am not too sure if many frontend developers are keen on working with it.&lt;/p&gt;

&lt;p&gt;I wish I would have had more time to prepare the talk and work on the example. There are still a lot of things I have no idea about and I hope I can find some time to continue learning. Even though I don&amp;#39;t foresee to use Clojure in a project at work anytime soon it can be very benificial to learn about new approaches - your perception of some language features can change.&lt;/p&gt;

&lt;p&gt;If you want to get started with Clojure as well - I can especially recommend &lt;a href=&quot;https://aphyr.com/posts/301-clojure-from-the-ground-up-welcome&quot;&gt;Aphyrs series on the basics&lt;/a&gt;. I have seen a useful talk by different InnoQ people over the years that especially covers the web libraries. &lt;a href=&quot;https://www.youtube.com/watch?v=3Xm_nVqxowk&quot;&gt;A recording by Michael Vitz is available on YouTube&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Centralized Logging Night Class</title>
   <link href="http://blog.florian-hopf.de/2017/09/elastic-night-class.html"/>
   <updated>2017-09-22T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2017/09/elastic-night-class</id>
   <content type="html">&lt;p&gt;This week my colleague Joanna and I were running a &lt;a href=&quot;https://www.meetup.com/ElasticSG/events/243138279/&quot;&gt;night class on centralized logging&lt;/a&gt; at the &lt;a href=&quot;https://www.meetup.com/ElasticSG/&quot;&gt;elastic meetup Singapore&lt;/a&gt; for the first time. We had lots of help of the organizer Alberto who also managed to get a room at his employer Pivotal Labs. Pizza was sponsored by my employer &lt;a href=&quot;http://zenika.sg&quot;&gt;Zenika&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our intention was to have people get in touch with centralized logging and the elastic stack for the first time by conduction a 2 hour workshop. The reception was very good, besides the 60 people registered there were also 60 on the waiting list. In the end around 30 people turned up which is the expected amount - like in many other cities in Singapore more people sign up for events than actually show up.&lt;/p&gt;

&lt;p&gt;We started off with an introduction to the topic where all of the components and how they play together were introduced in a talk. We covered Filebeat, Logstash, Kibana and the different scaling mechanisms. Conveniently we could take slides that we normally use for trainings. Some elastic guys were present as well and helped with questions on newer features.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/elastic-nightclass-workshop.png&quot; alt=&quot;Workshop&quot;&gt;&lt;/p&gt;

&lt;p&gt;After that we had a Pizza break before diving into the exercises. We chose a very simple setup of parsing and indexing access logs generated by &lt;a href=&quot;https://github.com/kiritbasu/Fake-Apache-Log-Generator&quot;&gt;a script&lt;/a&gt;. We provided the participants with instructions as well as the slides in a Github repo. Joanna and me and some of the elastic folks tried to help the people when struggling.&lt;/p&gt;

&lt;p&gt;Some of the questions I can remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deciding when to use Logstash or when to send data to elasticsearch directly.&lt;/li&gt;
&lt;li&gt;How to secure the system. How to do alerting. Those questions will surely be appreciated by elastic. &lt;/li&gt;
&lt;li&gt;How long does it take to get an intital system up and running.&lt;/li&gt;
&lt;li&gt;Which kind of logs can be parsed automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the problems people had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finding the right artifact to download from the elastic website (Linux tar.gz downloaded for mac, finding the way around the website, ...)&lt;/li&gt;
&lt;li&gt;Startup problems due to user rights: Starting the system as root (which elasticsearch will not allow), start with a user that has no rights to read the configuration&lt;/li&gt;
&lt;li&gt;Finding the right script to start elasticsearch (no .sh extension)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most common issue I noticed during trainings, whitespace in yaml files, didn&amp;#39;t play a role at all. Maybe we didn&amp;#39;t do enough configuration changes yet.&lt;/p&gt;

&lt;p&gt;There are a few things I would do differently for the next event.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have a shorter break. People enjoy socializing but we didn&amp;#39;t have enough time left for the exercises.&lt;/li&gt;
&lt;li&gt;Ease the setup: Maybe provide a container instead of letting the user set up all of the components.&lt;/li&gt;
&lt;li&gt;Introduce elasticsearch. That is something we just didn&amp;#39;t do.&lt;/li&gt;
&lt;li&gt;Show people how to use the console in Kibana to do simple queries to elasticsearch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/files/elastic-nightclass-pizza.png&quot; alt=&quot;Pizza&quot;&gt;&lt;/p&gt;

&lt;p&gt;Most of the people seemed to be happy about the event and for us as the speakers it&amp;#39;s a great way to get to know many different people. There&amp;#39;s a lot more interaction than when just doing an upfront talk. We are planning to do more events like this in the future. &lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Elasticsearch and the Languages of Singapore</title>
   <link href="http://blog.florian-hopf.de/2017/09/elasticsearch-languages-singapore.html"/>
   <updated>2017-09-04T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2017/09/elasticsearch-languages-singapore</id>
   <content type="html">&lt;p&gt;In June I gave a short talk at the first edition of &lt;a href=&quot;https://voxxeddays.com/singapore/&quot;&gt;Voxxed Days Singapore&lt;/a&gt; on using Elasticsearch to search the different languages of Singapore. This is a transcript of the talk, a video recording is available as well. We'll first look at some details of the data storage in elasticsearch before we see how it can be used to search the four official languages of Singapore.&lt;/p&gt;

&lt;p&gt;If you are already familiar with Elasticsearch you can also jump to the &lt;a href=&quot;#languages-singapore&quot;&gt;section on the different languages&lt;/a&gt; directly.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/QC2JwQimdoM&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;h2&gt;Elasticsearch&lt;/h2&gt;

&lt;p&gt;Elasticsearch is a distributed search engine, communication, queries and configuration is mainly done using HTTP and JSON. It is written in Java and based on the popular library Lucene. Mostly by means of Lucene Elasticsearch provides support for searching a multitude of natural languages and that is what we are looking at in this article.&lt;/p&gt;

&lt;p&gt;Getting started with Elasticsearch is pretty easy. On their website you can download different archives that you can just unpack. They contain scripts that can then be executed, the only prerequesite is a recent version of the Java Virtual Machine.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.1.zip
&lt;span class=&quot;c&quot;&gt;# zip is for Windows and Linux&lt;/span&gt;
unzip elasticsearch-5.4.1.zip
elasticsearch-5.4.1/bin/elasticsearch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once started you can directly access the HTTP interface of Elasticsearch on the default port 9200.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.florian-hopf.de/artikel-vortraege/elasticsearch-languages-singapore-voxxed/images/elasticsearch-up.png&quot; alt=&quot;response of intital request to elasticsearch&quot;&gt; &lt;/p&gt;

&lt;p&gt;Without any configuration you can then start writing data to the search index. I am using curl in the examples but you can use any HTTP client.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;curl -XPOST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:9200/voxxed/doc&quot;&lt;/span&gt; -d &lt;span class=&quot;s1&quot;&gt;'
&amp;gt; {
&amp;gt; &quot;title&quot;: &quot;Hello world!&quot;,
&amp;gt; &quot;content&quot;: &quot;Hello Voxxed Days Singapore!&quot;
&amp;gt; }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are posting a simple JSON document that contains two fields: &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;content&lt;/code&gt;. The url contains two path fragments that describe the index name (&lt;code&gt;voxxed&lt;/code&gt;), a logical collection of documents and the type (&lt;code&gt;doc&lt;/code&gt;) that determines how the documents are stored internally.&lt;/p&gt;

&lt;p&gt;Now that the data is stored we can immediately search it.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;curl -XPOST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:9200/voxxed/doc/_search&quot;&lt;/span&gt; -d &lt;span class=&quot;s1&quot;&gt;'
&amp;gt; {
&amp;gt; &quot;query&quot;: {
&amp;gt;   &quot;match&quot;: {
&amp;gt;     &quot;content&quot;: &quot;Singapore&quot;
&amp;gt;   }
&amp;gt; }
&amp;gt; }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are again posting a JSON document, this time appending &lt;code&gt;_search&lt;/code&gt; to the url. The body of the request contains a query in a json structure, the so called query dsl. Simply put this searches for all documents in the index that contain the &lt;code&gt;Singapore&lt;/code&gt; in the &lt;code&gt;content&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;This returns another json structure that, among other information, contains the document we indexed initially.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;took&quot;&lt;/span&gt; : 127,
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;, 
  &lt;span class=&quot;s2&quot;&gt;&quot;hits&quot;&lt;/span&gt; : &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;total&quot;&lt;/span&gt; : 1,
    &lt;span class=&quot;s2&quot;&gt;&quot;max_score&quot;&lt;/span&gt; : 0.2876821,
    &lt;span class=&quot;s2&quot;&gt;&quot;hits&quot;&lt;/span&gt; : &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;_index&quot;&lt;/span&gt; : &lt;span class=&quot;s2&quot;&gt;&quot;voxxed&quot;&lt;/span&gt;,
        &lt;span class=&quot;s2&quot;&gt;&quot;_type&quot;&lt;/span&gt; : &lt;span class=&quot;s2&quot;&gt;&quot;doc&quot;&lt;/span&gt;,
        &lt;span class=&quot;s2&quot;&gt;&quot;_id&quot;&lt;/span&gt; : &lt;span class=&quot;s2&quot;&gt;&quot;AVwAP4Aw9lCQvRKyIhgJ&quot;&lt;/span&gt;,
        &lt;span class=&quot;s2&quot;&gt;&quot;_score&quot;&lt;/span&gt; : 0.2876821,
        &lt;span class=&quot;s2&quot;&gt;&quot;_source&quot;&lt;/span&gt; : &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;title&quot;&lt;/span&gt; : &lt;span class=&quot;s2&quot;&gt;&quot;Hello world!&quot;&lt;/span&gt;,
          &lt;span class=&quot;s2&quot;&gt;&quot;content&quot;&lt;/span&gt; : &lt;span class=&quot;s2&quot;&gt;&quot;Hello Voxxed Days Singapore!&quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So how does searching work internally? Elasticsearch provides an inverted index that is used for the lookup of the search terms. For our simple example the inverted index for the &lt;code&gt;content&lt;/code&gt; field looks similar to this.&lt;/p&gt;

&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Term&lt;/th&gt;&lt;th&gt;Doc Id&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;days&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;hello&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;singapore&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;voxxed&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Each word of our phrase &lt;code&gt;Hello Voxxed Days Singapore!&lt;/code&gt; is stored with a pointer to the document it occurs in. Words are extracted, all punctuation is removed and the words are being lowercased. When a search for a term is being executed the same processing is being done and there is a direct lookup of the result in the index.&lt;/p&gt;

&lt;p&gt;The process of preparing the content for storage is called analyzing and will be different for different kinds of data and applications. It is encapsulated in an analyzer that processes the incoming text, tokenizes it using a Tokenizer and processes it using optional TokenFilters. By default the Tokenizer splits on word boundaries and there is a filter that lowercases the content.   &lt;/p&gt;

&lt;p&gt;The analyzing process is also where the language specific processing can happen. There are some prebuilt Analyzers for different languages available that can do different things like character normalization or stemming, which is an algorithmic process that tries to reduce words to their base form. Besides using the analyzers that are shipped with elasticsearch you can also define custom analyzers that use some of the predefined tokenizers and token filter.&lt;/p&gt;

&lt;p&gt;Analyzers need to be configured upfront in the mapping before documents are stored in the index. To configure an english analyzer for the &lt;code&gt;content&lt;/code&gt; field we can issue the following PUT request.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;curl -XPUT &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:9200/voxxed_en&quot;&lt;/span&gt; -d&lt;span class=&quot;s1&quot;&gt;'
{
  &quot;mappings&quot;: {
    &quot;doc&quot;: {
      &quot;properties&quot;: {
        &quot;content&quot;: {
          &quot;type&quot;: &quot;text&quot;, 
          &quot;analyzer&quot;: &quot;english&quot;
        }
      }  
    }
  }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There&amp;#39;s a new index name but the name of the type is the same. This is a common strategy to handle multilingual content. Have one index per language but with the same structure.&lt;/p&gt;

&lt;p&gt;Using this mapping we can now search for the term &lt;code&gt;day&lt;/code&gt; instead of &lt;code&gt;days&lt;/code&gt; as well. The analyzing does some normalization on the words so that each of them can be found. In general the way a word is stored in the index influences the search quality a lot and is the common way to allow users to search for words in different ways.&lt;/p&gt;

&lt;h2 id=&quot;languages-singapore&quot;&gt;Languages of Singapore&lt;/h2&gt;

&lt;p&gt;There are four official languages in Singapore&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;English&lt;/li&gt;
&lt;li&gt;Malay&lt;/li&gt;
&lt;li&gt;Mandarin&lt;/li&gt;
&lt;li&gt;Tamil &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have already seen how we can search for english content using the english analyzer. Let&amp;#39;s look at Malay language next.&lt;/p&gt;

&lt;h2&gt;Malay&lt;/h2&gt;

&lt;p&gt;Malay is the national language of Singapore, that is why the national anthem is also in Malay. It starts with the following lines&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mari kita rakyat Singapura
Sama-sama menuju bahagia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Malay uses the latin alphabet, words are separated by whitespace. We can just index this text as it is.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;curl -XPOST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:9200/voxxed/doc&quot;&lt;/span&gt; -d&lt;span class=&quot;s1&quot;&gt;'
{
  &quot;title&quot;: &quot;Majulah Singapura&quot;,
  &quot;content&quot;: &quot;Mari kita rakyat Singapura Sama-sama menuju bahagia&quot;
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can immediately search this just like we searched english language text. The standard analyzer prepares the words as they are for the index.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;curl -XPOST &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:9200/voxxed/doc/_search&quot;&lt;/span&gt; -d&lt;span class=&quot;s1&quot;&gt;'
{
  &quot;query&quot;: {
    &quot;match&quot;: {
      &quot;content&quot;: &quot;bahagia&quot;
    }
  }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are searching for &lt;code&gt;bahagia&lt;/code&gt; which means happiness. The document is found as expected.&lt;/p&gt;

&lt;p&gt;For malay there is no language specific analyzer available but the standard analyzer works fine. Malay doesn&amp;#39;t have a lot of word inflection but has some prefix and suffix rules. What could be possible is to process the text with the Indonesian stemmer that is availabe. Both languages share many rules but there might also be exceptions. &lt;/p&gt;

&lt;h2&gt;Tamil&lt;/h2&gt;

&lt;p&gt;Tamil is a bit differnt as it is written using a different script.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ஆறின கஞ்சி பழங் கஞ்சி&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a proverb saying &lt;em&gt;Cold food is soon old food&lt;/em&gt;. Even if you can&amp;#39;t read it you can see that the second and fourth word are the same, meaning food. &lt;/p&gt;

&lt;p&gt;We can again index this content and then search it.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;query&quot;&lt;/span&gt;: &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;match&quot;&lt;/span&gt;: &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;content&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;கஞ்சி&quot;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There is no special handling for Tamil. The standard tokenizer splits words correctly. It doesn&amp;#39;t matter that it&amp;#39;s a different script as elasticsearch compares the words on the byte level.&lt;/p&gt;

&lt;h2&gt;Mandarin&lt;/h2&gt;

&lt;p&gt;Mandarin is very different from the others as it uses a different script and no white space. My creativity is decreasing so this just means &lt;em&gt;Hello Singapore&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;你好新加坡&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you index this using the standard analyzer it will split to single characters. When using this for search this can lead to a lot of false positives. That&amp;#39;s why there are alternatives available, most notably the CJKAnalyzer that can work on chinese, japanese and korean language and builds bigrams of the characters.&lt;/p&gt;

&lt;p&gt;For the example this leads to &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;你好 &lt;/li&gt;
&lt;li&gt;好新 &lt;/li&gt;
&lt;li&gt;新加 &lt;/li&gt;
&lt;li&gt;加坡&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even this can lead to invalid or irrelevant words but it is better than searching for single characters. When searching for the word Singapore the document is found correctly.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;match&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;新加坡&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;An alternative can be to use the Smart Chinese Plugin that is, like the name suggest, smarter than just building bigrams. It uses a probabilistic approach to determine sentence and word boundaries.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Each of the languages of Singapore has its specialities. There is basic support for all of them in Elasticsearch but working with multiple languages can be challenging, in real life as well as in search engines.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Spring Security and Multiple Filter Chains</title>
   <link href="http://blog.florian-hopf.de/2017/08/spring-security.html"/>
   <updated>2017-08-21T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2017/08/spring-security</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/&quot;&gt;Spring Security&lt;/a&gt; is an immensely useful technology. It allows you to secure your application without being too intrusive and allows to plug with many different authentication mechanisms. On the other hand it is not that easy to get into and one of those tools that I have to relearn each time I am touching it. In this post I'll describe some of the basics of spring security and how you can use it to secure different parts of your application in different ways.&lt;/p&gt;

&lt;h2&gt;Spring Security Configuration&lt;/h2&gt;

&lt;p&gt;Let&amp;#39;s look at a piece of configuration for Spring Security, you can find the &lt;a href=&quot;https://github.com/fhopf/springsecuriy-example&quot;&gt;full source code on Github&lt;/a&gt;. I am using Spring Boot but most parts should be the same for all Spring applications.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SecurityConfig&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebSecurityConfigurerAdapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpSecurity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;httpBasic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorizeRequests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/secret/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authenticated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorizeRequests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;permitAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the simplest case you just configure the &lt;code&gt;HttpSecurity&lt;/code&gt; using the method chaining that is common in Spring Security. In this case we enable HTTP Basic Auth and require authentication for one endpoint (everything below &lt;code&gt;/secure/&lt;/code&gt;). All other requests (denoted by &lt;code&gt;/**&lt;/code&gt;) will be permitted. The patterns that are used here are of the Ant path syntax but you can also use different &lt;a href=&quot;http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/web/util/matcher/RequestMatcher.html&quot;&gt;&lt;code&gt;RequestMatcher&lt;/code&gt;&lt;/a&gt; to decide which parts of your application require which authentication.&lt;/p&gt;

&lt;p&gt;All the functionality of Spring boot is implemented in a filter chain. The call to &lt;code&gt;httpBasic()&lt;/code&gt; above actually just makes sure that the relevant filter is added to the filter chain.  In this case the &lt;code&gt;BasicAuthenticationFilter&lt;/code&gt; will check if there is an &lt;code&gt;Authorization&lt;/code&gt; header and evaluate it. If one is found it will add an &lt;code&gt;Authentication&lt;/code&gt; object to the context and execute the rest of the filter chain. At the end of the chain is the &lt;code&gt;FilterSecurityInterceptor&lt;/code&gt; that checks if the requested resource requires authentication and if the one that is set conforms to the requested roles.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/spring-security-filter.png&quot; alt=&quot;Filter Chain&quot;&gt;&lt;/p&gt;

&lt;p&gt;You can also exclude some parts of the application from authentication by configuring the &lt;code&gt;WebSecurity&lt;/code&gt;. The following method makes sure that any requests to &lt;code&gt;/resources/&lt;/code&gt; skip the configuration above.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebSecurity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ignoring&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/resources/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Under the hood this will add an additional filter chain that is triggered for the path configured and does nothing.&lt;/p&gt;

&lt;h2&gt;Multiple Filter Chains&lt;/h2&gt;

&lt;p&gt;Sometimes it can be necessary to use different authentication mechanisms for different parts of your application. To achieve that, Spring Security allows you to add several configuration objects. It is a common practice to use inner configuration classes for this that can also share some parts of the enclosing application. The following class adds two different Spring Security filter chains.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SecurityConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Configuration&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiConfiguration&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebSecurityConfigurerAdapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpSecurity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// doesn't really make sense to protect a REST API using form login but it is just for illustration&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;formLogin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorizeRequests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/secret/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authenticated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorizeRequests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;permitAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebSecurity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ignoring&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/resources/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Order&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Configuration&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActuatorConfiguration&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebSecurityConfigurerAdapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpSecurity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/management/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;httpBasic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorizeRequests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;antMatchers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/management/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authenticated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebSecurity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Both of the classes inherit from the adapter configuration class and configure their &lt;code&gt;HttpSecurity&lt;/code&gt;. Each of those classes adds a filter chain and the first one that matches is executed. The &lt;code&gt;@Order&lt;/code&gt; annotation can be used to influence the order of the filter chains to make sure that the right one is executed first. &lt;/p&gt;

&lt;p&gt;It can also be necessary to restrict the filter chain to only a certain part of the application so that it is not triggered for other parts. The &lt;code&gt;ActuatorConfiguration&lt;/code&gt; is restricted to only match requests to &lt;code&gt;/management/&lt;/code&gt;.  Be aware that there are two different places in the configuration that accept a &lt;code&gt;RequestMatcher&lt;/code&gt;. The one at the beginning restricts the url the filter chain is triggered for. The ones after &lt;code&gt;authorizeRequests()&lt;/code&gt; are used to define which requests require what kind of authentication.  &lt;/p&gt;

&lt;p&gt;Note that configuring the &lt;code&gt;WebSecurity&lt;/code&gt; is not tied to one of the &lt;code&gt;HttpSecurity&lt;/code&gt; configurations as those add their own filter chain, only the order might be different. If you add a pattern in both configurations it will even operate on the same instance of &lt;code&gt;WebSecurity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One last thing: In case you are using a custom authentication filter (e.g. for token based authentication) you might have to take care that you don&amp;#39;t register your filter as a Servlet Filter as well. You can influence that by configuring a method returning a &lt;code&gt;FilterRegistrationBean&lt;/code&gt; and accepting an instance of your &lt;code&gt;Filter&lt;/code&gt;. just create a new &lt;code&gt;FilterRegistrationBean&lt;/code&gt; for your filter and set &lt;code&gt;enabled&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Traffic Light Visualizations for Kibana</title>
   <link href="http://blog.florian-hopf.de/2017/04/kibana-traffic-lights.html"/>
   <updated>2017-04-03T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2017/04/kibana-traffic-lights</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://www.elastic.co/blog/kibana-5-3-0-released&quot;&gt;Kibana 5.3&lt;/a&gt; shipped with an interesting feature that had been anticipated for quite a while. It provides the ability to display the latest value for a certain field using the Top Hit Aggregation. I'll show how to use the Top Hit Aggregation to create two different visualizations.&lt;/p&gt;

&lt;p&gt;But first, of course you need some timestamped data. The following documents are minimal examples.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;POST /logstash-2017.03.30/log
{
  &quot;metric&quot;: 0.6,
  &quot;ip&quot;: &quot;192.168.0.1&quot;,
  &quot;@timestamp&quot;: &quot;2017-03-30T09:10:22.611Z&quot;
}

POST /logstash-2017.03.30/log
{
  &quot;metric&quot;: 0.4,
  &quot;ip&quot;: &quot;192.168.0.2&quot;,
  &quot;@timestamp&quot;: &quot;2017-03-30T09:10:22.611Z&quot;
}

POST /logstash-2017.03.30/log
{
  &quot;metric&quot;: 0.7,
  &quot;ip&quot;: &quot;192.168.0.1&quot;,
  &quot;@timestamp&quot;: &quot;2017-03-30T09:10:22.611Z&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Besides the timestamp there are two fields for each document: metric (which could be anything, e.g. used disk space, load or anything else) and ip (which is an identifier for a machine). There are two values for the ip &lt;code&gt;192.168.0.1&lt;/code&gt; (0.6, 0.7) and one for &lt;code&gt;192.168.0.2&lt;/code&gt; (0.4).&lt;/p&gt;

&lt;h2&gt;Metric Visualization&lt;/h2&gt;

&lt;p&gt;The easiest way to use the new aggregation type is by just displaying the latest value in a widget. You can create a new visualization using a metric, choosing Top Hit as aggregation. You can choose how many latest values you want to take into account (1 for only the latest) and how to aggregate them. By default the values will be sorted on timestamp in descending order which you can also change to display the first value.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/kibana-top-hit-metric-2.png&quot; alt=&quot;Top Hit Metric Aggregation&quot;&gt;&lt;/p&gt;

&lt;p&gt;Of course you can also tie this visualization to a search, querying for the ip &lt;code&gt;192.168.0.2&lt;/code&gt; will then only display the latest value for this certain ip.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/kibana-top-hit-metric-3.png&quot; alt=&quot;Top Hit Metric Aggregation&quot;&gt;&lt;/p&gt;

&lt;h2&gt;Heatmap Visualization&lt;/h2&gt;

&lt;p&gt;A more visual approach to displaying the latest value can be done using a heatmap. You can build a traffic light style dashboard of any values in your system. &lt;/p&gt;

&lt;p&gt;The Top Hit can be registered as a metric aggregation in the first section of the heatmap configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/kibana-heatmap-top-hit.png&quot; alt=&quot;Top Hit Metric Aggregation&quot;&gt;&lt;/p&gt;

&lt;p&gt;The buckets on the X-Axis can then be determined by a Terms aggregation on the ip field, displaying separate sections for each ip.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/kibana-heatmap-terms.png&quot; alt=&quot;Top Hit Metric Aggregation&quot;&gt;&lt;/p&gt;

&lt;p&gt;Finally, the color to display for the different values can be configured on the options tab. You can create custom ranges that can then be assigned to a certain color in the legend on the right.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/kibana-heatmap-options.png&quot; alt=&quot;Top Hit Metric Aggregation&quot;&gt;&lt;/p&gt;

&lt;p&gt;These visualizations can help a lot to immediately judge the health of your system.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Changes</title>
   <link href="http://blog.florian-hopf.de/2017/01/changes.html"/>
   <updated>2017-01-24T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2017/01/changes</id>
   <content type="html">&lt;p&gt;I came to Karlsruhe for my studies, started working at &lt;a href=&quot;http://synyx.de&quot;&gt;synyx&lt;/a&gt; and stayed here after switching to working as an independent developer and consultant. For years I helped running the &lt;a href=&quot;http://jug-ka.de&quot;&gt;local Java User Group&lt;/a&gt;, I am one of the founders of the &lt;a href=&quot;&quot;&gt;Search Meetup Karlsruhe&lt;/a&gt; and most of my clients are from the area as well. Though my wife and myself liked it here we decided it's time for a change: We'll be relocating to Singapore for at least a year.&lt;/p&gt;

&lt;p&gt;I am glad to announce that I&amp;#39;ll start working as a developer at &lt;a href=&quot;http://zenika.sg&quot;&gt;Zenika Singapore&lt;/a&gt;. I guess I&amp;#39;ll still be doing search projects and hope that I can continue blogging from time to time. My consulting business will shut down at the beginning of February but if you would like to work with me that is still possible - you just have to talk to Zenika instead of myself. Thanks to all former colleagues, user group members and friends. I&amp;#39;d be happy to hear from you when you are in the area. &lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java Clients for Elasticsearch Transcript</title>
   <link href="http://blog.florian-hopf.de/2016/11/java-clients-elasticsearch.html"/>
   <updated>2016-11-09T00:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/11/java-clients-elasticsearch</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This is a transcript of a &lt;a href=&quot;http://www.meetup.com/de-DE/singajug/events/234987866/&quot;&gt;talk I gave at the Singapore Java User Group on November 9 2016&lt;/a&gt;. It can also be seen as an updated version of &lt;a href=&quot;https://www.found.no/foundation/java-clients-for-elasticsearch/&quot;&gt;an article with the same name I published in 2014 on the Found blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this talk I will introduce three different clients for elasticsearch as well as Spring Data Elasticsearch. But to get started let&amp;#39;s look at some of the basics of elasticsearch.&lt;/p&gt;

&lt;h2&gt;elasticsearch&lt;/h2&gt;

&lt;p&gt;To introduce elasticsearch I am using a definition that is taken directly from the elastic website.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Elasticsearch is a distributed, JSON-based search and analytics engine, designed for horizontal scalability, maximum reliability, and easy management.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let&amp;#39;s see first what a &lt;em&gt;JSON-based search and analytics engine&lt;/em&gt; means.&lt;/p&gt;

&lt;p&gt;To understand what elasticsearch does it&amp;#39;s good to see an example of a search page. This is something everybody is familiar with, the code search on Github.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/github.png&quot; alt=&quot;Github Screenshot&quot;&gt;&lt;/p&gt;

&lt;p&gt;Keywords can be entered in a single search input, below is a list of results. One of the distinguishing features between a search engine and other databases is that there is a notion of relevance. We can see that for our search term &lt;code&gt;elasticsearch&lt;/code&gt; the project for the search engine is on the first place. It&amp;#39;s very likely that people are looking for the project when searching for this term. The factors that are used to determine if a result is more relevant than another can vary from application to application - I don&amp;#39;t know what Github is doing but I can imagine that they are using factors like popularity besides classical text relevance features. There are a lot more features on the website that a classical search engine like elasitcsearch supports: Highlighting the occurance in the result, paginate the list and sort using different criteria. On the left you can see the so called facets that can be used to further refine the result list using criteria from the documents found. This is similar to features found on ecommerce sites like ebay and Amazon. For doing something like this there is the aggregation feature in elasticsearch that is also the basis for its analytics capabilities. This and a lot more can be done using elasticsearch as well. In this case this is even more obvious - Github is actually using elasticsearch for searching through the large amount of data they are storing.&lt;/p&gt;

&lt;p&gt;If you want to build a search application like this you have to install the engine first. Fortunately elasticsearch is really easy to get started with. There is no special requirement besides a recent Java runtime. You can download the elasticsearch archive from the elastic website, unpack it and start elasticsearch using a script.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;# download archive
wget https://artifacts.elastic.co/downloads/
    elasticsearch/elasticsearch-5.0.0.zip

unzip elasticsearch-5.0.0.zip

# on windows: elasticsearch.bat
elasticsearch-5.0.0/bin/elasticsearch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For production use there are also packages for different Linux distributions. You can see that elasticsearch is started by doing a HTTP GET request on the standard port. In the examples I am using curl, the command line client for doing HTTP requests, that is available for a lot of environments.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;curl -XGET &quot;http://localhost:9200&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;elasticsearch will answer this request with a JSON document that contains some information on the installation.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;LI8ZN-t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;cluster_name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;elasticsearch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;cluster_uuid&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;UvbMAoJ8TieUqugCGw7Xrw&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;number&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;5.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;build_hash&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;253032b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;build_date&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2016-10-26T04:37:51.531Z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;build_snapshot&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;lucene_version&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;6.2.0&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;tagline&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;You Know, for Search&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The most important fact for us is that we can see that the server is started. But there is also versioning information on elasticsearch and Lucene, the underlying library used for most of the search functionality.&lt;/p&gt;

&lt;p&gt;If we now want to store data in elasticsearch we send it as a JSON document as well, this time using a POST request. As I really like the food in Singapore I want to build an application that allows me to search my favorite food. Let&amp;#39;s index the first dish.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;curl -XPOST &quot;http://localhost:9200/food/dish&quot; -d'
{
  &quot;food&quot;: &quot;Hainanese Chicken Rice&quot;,
  &quot;tags&quot;: [&quot;chicken&quot;, &quot;rice&quot;],
  &quot;favorite&quot;: {
    &quot;location&quot;: &quot;Tian Tian&quot;,
    &quot;price&quot;: 5.00
  }
}'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are using the same port we used before, this time we just add two more fragments to the url: &lt;code&gt;food&lt;/code&gt; and &lt;code&gt;dish&lt;/code&gt;. The first is the name of the index, a logical collection of documents. The second is the type. It determines the structure of the document we are saving, the so called mapping.&lt;/p&gt;

&lt;p&gt;The dish itself is modeled as a document. elasticsearch supports different data types like string, that is used for the &lt;code&gt;food&lt;/code&gt; attribute, a list like in &lt;code&gt;tags&lt;/code&gt; and even embedded documents like the &lt;code&gt;favorite&lt;/code&gt; document. Besides that there are more primitive types like numerics, booleans and specialized types like geo coordinates.&lt;/p&gt;

&lt;p&gt;We can now index another document doing another POST request.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;curl -XPOST &quot;http://localhost:9200/food/dish&quot; -d'
{
  &quot;food&quot;: &quot;Ayam Penyet&quot;,
  &quot;tags&quot;: [&quot;chicken&quot;, &quot;indonesian&quot;],
  &quot;spicy&quot;: true
}'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The structure of this document is a bit different. It doesn&amp;#39;t contain the&lt;code&gt;favorite&lt;/code&gt; subdocument but has another attribute &lt;code&gt;spicy&lt;/code&gt; instead. Documents of the same kind can be very different - but keep in mind that you need to interpret some parts in your application. Normally you will have similar documents.&lt;/p&gt;

&lt;p&gt;With those documents indexed it is automatically possible to search them. One option is to do a GET request on &lt;code&gt;/_search&lt;/code&gt; and add the query term as a parameter.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;curl -XGET &quot;http://localhost:9200/food/dish/_search?q=chicken&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Searching for chicken in both documents also returns both of them. This is an excerpt of the result.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;...
{&quot;total&quot;:2,&quot;max_score&quot;:0.3666863,&quot;hits&quot;:[{&quot;_index&quot;:&quot;food&quot;,&quot;_type&quot;:&quot;dish&quot;,&quot;_id&quot;:&quot;AVg9cMwARrBlrY9tYBqX&quot;,&quot;_score&quot;:0.3666863,&quot;_source&quot;:
{
  &quot;food&quot;: &quot;Hainanese Chicken Rice&quot;,
  &quot;tags&quot;: [&quot;chicken&quot;, &quot;rice&quot;],
  &quot;favorite&quot;: {
    &quot;location&quot;: &quot;Tian Tian&quot;,
    &quot;price&quot;: 5.00
  }
}},
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There is some global information like the amount of documents found. But the most important property is the &lt;code&gt;hits&lt;/code&gt; array that contains the original source of our indexed dishes.&lt;/p&gt;

&lt;p&gt;It&amp;#39;s very easy to get started like this but most of the time the queries will be more complex. That&amp;#39;s why elasticsearch provides the query DSL, a JSON structure that describes a query as well as any other search features that are requested.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;curl -XPOST &quot;http://localhost:9200/food/dish/_search&quot; -d'
{
  &quot;query&quot;: {
    &quot;bool&quot;: {
      &quot;must&quot;: {
      &quot;match&quot;: {
        &quot;_all&quot;: &quot;rice&quot;
      }
      },
      &quot;filter&quot;: {
    &quot;term&quot;: {
      &quot;tags.keyword&quot;: &quot;chicken&quot;
    }
      }
    }
  }
}'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are searching for all documents that contain the term &lt;code&gt;rice&lt;/code&gt; and also have &lt;code&gt;chicken&lt;/code&gt; in &lt;code&gt;tags&lt;/code&gt;. Accessing a field using the &lt;code&gt;.keyword&lt;/code&gt; allows to do an exact search and is a new feature in elasticsearch 5.0.&lt;/p&gt;

&lt;p&gt;Besides the search itself you can use the query DSL to request more information from elasticsearch, be it something like highlighting or autocompletion or the aggregations that can be used to build a faceting feature.&lt;/p&gt;

&lt;p&gt;Let&amp;#39;s move on to another part of the definition.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Elasticsearch is [...] distributed [...], designed for horizontal scalability, maximum reliability &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So far we have only accessed a single elasticsearch instance.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/access-simple.png&quot; alt=&quot;Accessing a single node&quot;&gt;&lt;/p&gt;

&lt;p&gt;Our application would be talking directly to that node. Now, as elasticsearch is designed for horizontal scalability we can also add more nodes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/access-cluster.png&quot; alt=&quot;Accessing a cluster&quot;&gt;&lt;/p&gt;

&lt;p&gt;The nodes form a cluster. We can still talk to the first node and it will distribute all requests to the necessary nodes of the cluster. This is completely transparent to us. &lt;/p&gt;

&lt;p&gt;Building a cluster with elasticsearch is really easy at the beginning but of course it can be more challenging to maintain a production cluster.&lt;/p&gt;

&lt;p&gt;Now that we have a basic understanding about what elasticsearch does let&amp;#39;s see how we can access it from a Java application.&lt;/p&gt;

&lt;h2&gt;Transport Client&lt;/h2&gt;

&lt;p&gt;The transport client has been available from the beginning and is the client chosen most frequently. Starting with elasticsearch 5.0 it has its own artifact that can be integrated in your build, e.g. using Gradle.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;dependencies {
    compile group: 'org.elasticsearch.client',
        name: 'transport',
        version: '5.0.0'
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All functionality of Elasticsearch is available using the &lt;code&gt;Client&lt;/code&gt; interface, a concrete instance is the &lt;code&gt;TransportClient&lt;/code&gt;, that can be instanciated using a &lt;code&gt;Settings&lt;/code&gt; object and can have one or more addresses of elasticsearch nodes.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;TransportAddress address =
    new InetSocketTransportAddress(
        InetAddress.getByName(&quot;localhost&quot;), 9300);

Client client = new PreBuiltTransportClient(Settings.EMPTY)
    addTransportAddress(address);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;client&lt;/code&gt; then provides methods for different features of elasticsearch. First, let&amp;#39;s search again. Recall the structure of the query we issued above.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;curl -XPOST &quot;http://localhost:9200/food/dish/_search&quot; -d'
{
  &quot;query&quot;: {
    &quot;bool&quot;: {
      &quot;must&quot;: {
      &quot;match&quot;: {
        &quot;_all&quot;: &quot;rice&quot;
      }
      },
      &quot;filter&quot;: {
    &quot;term&quot;: {
      &quot;tags.keyword&quot;: &quot;chicken&quot;
    }
      }
    }
  }
}'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;bool&lt;/code&gt; query that has a &lt;code&gt;match&lt;/code&gt; query in its &lt;code&gt;must&lt;/code&gt; section and a &lt;code&gt;term&lt;/code&gt; query in its &lt;code&gt;filter&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;Luckily once you have a query like this you can easily transform it to the Java equivalent.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;SearchResponse searchResponse = client
   .prepareSearch(&quot;food&quot;)
   .setQuery(
    boolQuery().
      must(matchQuery(&quot;_all&quot;, &quot;rice&quot;)).
      filter(termQuery(&quot;tags.keyword&quot;, &quot;chicken&quot;)))
   .execute().actionGet();

assertEquals(1, searchResponse.getHits().getTotalHits());

SearchHit hit = searchResponse.getHits().getAt(0);
String food = hit.getSource().get(&quot;food&quot;).toString();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are requesting a &lt;code&gt;SearchSourceBuilder&lt;/code&gt; by calling &lt;code&gt;prepareSearch&lt;/code&gt; on the &lt;code&gt;client&lt;/code&gt;. There we can set a query using the static helper methods. And again, it&amp;#39;s a &lt;code&gt;bool&lt;/code&gt; query that has a &lt;code&gt;match&lt;/code&gt; query in its &lt;code&gt;must&lt;/code&gt; section and a &lt;code&gt;term&lt;/code&gt; query in its &lt;code&gt;filter&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;Calling &lt;code&gt;execute&lt;/code&gt; returns a Future object, &lt;code&gt;actionGet&lt;/code&gt; is the blocking part of the call. The &lt;code&gt;SearchResponse&lt;/code&gt; represents the same JSON structure we can see when doing a search using the HTTP interface. The source of the dish is then available as a map.&lt;/p&gt;

&lt;p&gt;When indexing data there are different options available. One is to use the &lt;code&gt;jsonBuilder&lt;/code&gt; to create a JSON representation.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;XContentBuilder builder = jsonBuilder()
    .startObject()
        .field(&quot;food&quot;, &quot;Roti Prata&quot;)
        .array(&quot;tags&quot;, new String [] {&quot;curry&quot;})
        .startObject(&quot;favorite&quot;)
        .field(&quot;location&quot;, &quot;Tiong Bahru&quot;)
        .field(&quot;price&quot;, 2.00)
        .endObject()
    .endObject();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It provides different methods that can be used to create the structure of the JSON document. This can then be used as the source for an IndexRequest.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;IndexResponse resp = client.prepareIndex(&quot;food&quot;,&quot;dish&quot;)
        .setSource(builder)
        .execute()
        .actionGet();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Besides using the &lt;code&gt;jsonBuilder&lt;/code&gt; there are several other options available. &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/indexing-source.png&quot; alt=&quot;Different methods for indexing&quot;&gt;&lt;/p&gt;

&lt;p&gt;A common option is to use a Map, the convenience methods that accept field name and value for simple structures or the option to pass in a String, often in combination with a library like Jackson for serialization.&lt;/p&gt;

&lt;p&gt;We have seen above that the Transport Client accepts the address of one or more elasticsearch nodes. You might have noticed that the port is different to the one used for http, 9300 instead of 9200. This is because the client doesn&amp;#39;t communicate via http - it connects to an existing cluster using the transport protocol, a binary protocol that is also used for inter node communication in a cluster.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/access-transport-client.png&quot; alt=&quot;Transport client uses binary protocol&quot;&gt;&lt;/p&gt;

&lt;p&gt;You might have noticed as well that so far we are only talking to one node of the cluster. Once this node goes down we might not be able to access our data anymore. If you need high availability you can enable the sniffing option that lets your client talk to multiple nodes in the cluster.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/access-transport-client-sniffing.png&quot; alt=&quot;Sniffing enables high availability&quot;&gt;&lt;/p&gt;

&lt;p&gt;Now when one of the nodes goes down, we can still access the data using the other nodes. The feature can be enabled by setting &lt;code&gt;client.transport.sniff&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; when creating the client.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;TransportAddress address =
    new InetSocketTransportAddress(
        InetAddress.getByName(&quot;localhost&quot;), 9300);

Settings settings = Settings.builder()
            .put(&quot;client.transport.sniff&quot;, true)
            .build();

Client client = new PreBuiltTransportClient(settings)
    addTransportAddress(address);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This feature works by requesting the current state of the cluster from the known node using one of the management APIs of elasticsearch. When configured this is done during startup and in an regular interval, by default every 5s.&lt;/p&gt;

&lt;p&gt;Sniffing is an important feature to make sure your application stay up even during node failure.&lt;/p&gt;

&lt;p&gt;When using the Transport Client you have some obvious benefits: As the client is shipped with the server (and even includes a dependency to the server) you can be sure that all of the current API is available for use in your client code. Communication is more efficient than JSON over HTTP and there is support for client side load balancing.&lt;/p&gt;

&lt;p&gt;On the other side there are some drawbacks as well: As the transport protocol is an internal protocol you need to use a compatible elasticsearch version on the server and the client. Also, rather unexpected, this also means that a similar JDK version needs to be used. Additionally you need to include all of the dependencies to elasticsearch in your application. This can be a huge problem, especially with larger existing applications. For example it might happen that a CMS already ships some version of Lucene. Often it is not possible to resolve dependency conflicts like this.&lt;/p&gt;

&lt;p&gt;Fortunately, there is a solution for this.&lt;/p&gt;

&lt;h2&gt;RestClient&lt;/h2&gt;

&lt;p&gt;elasticsearch 5.0 introduced a new client that uses the HTTP API of elasticsearch instead of the internal protocol. This requires far less dependencies. Also you don&amp;#39;t need to care about the version that much - the current client can be used with elasticsearch 2.x as well. &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/files/access-http.png&quot; alt=&quot;RestClient uses HTTP for talking to a cluster&quot;&gt;&lt;/p&gt;

&lt;p&gt;But there is also a drawback - it doesn&amp;#39;t have a lot of features yet.&lt;/p&gt;

&lt;p&gt;The client is available as a Maven artifact as well.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;dependencies {
    compile group: 'org.elasticsearch.client',
        name: 'rest',
        version: '5.0.0'
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The client only depends on the apache httpclient and its dependencies. This is a Gradle listing of all the dependencies.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;+--- org.apache.httpcomponents:httpclient:4.5.2
+--- org.apache.httpcomponents:httpcore:4.4.5
+--- org.apache.httpcomponents:httpasyncclient:4.1.2
+--- org.apache.httpcomponents:httpcore-nio:4.4.5
+--- commons-codec:commons-codec:1.10
\--- commons-logging:commons-logging:1.1.3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It can be instanciated by passing in one or more &lt;code&gt;HttpHost&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;RestClient restClient = RestClient.builder(
    new HttpHost(&quot;localhost&quot;, 9200, &quot;http&quot;),
    new HttpHost(&quot;localhost&quot;, 9201, &quot;http&quot;))
    .build();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As there is not a lot of functionality as of now most of the JSON is just available as a String. This is an example of executing a &lt;code&gt;match_all&lt;/code&gt; query and transform the response to a String using a helper method.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;  HttpEntity entity = new NStringEntity(
      &quot;{ \&quot;query\&quot;: { \&quot;match_all\&quot;: {}}}&quot;,
      ContentType.APPLICATION_JSON);
  // alternative: performRequestAsync
  Response response = restClient.performRequest(&quot;POST&quot;,                                     &quot;/_search&quot;, emptyMap(), entity);
  String json = toString(response.getEntity());
  // ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Indexing data is low level as well. You just send the String containing the JSON document to the endpoint. The client supports sniffing using a separate library. Besides the fact that there are less dependencies and the elasticsearch version is not as important anymore there is another benefit for operations: The cluster can now be separated from the applications with HTTP being the only protocol to talk to the cluster.&lt;/p&gt;

&lt;p&gt;Most of the functionality depends on the Apache http client directly. There is support for setting timeouts, using basic auth, custom headers and error handling.&lt;/p&gt;

&lt;p&gt;For now there is no query support. If you are able to add the elasticsearch dependency to your application (which of course voids some of the benefits again) you can use the &lt;code&gt;SearchSourceBuilder&lt;/code&gt; and related functionality to create Strings for the query.&lt;/p&gt;

&lt;p&gt;Besides the new RestClient there is also another HTTP client available that has more features: The community built client Jest.&lt;/p&gt;

&lt;h2&gt;Jest&lt;/h2&gt;

&lt;p&gt;Jest is available for a long time already and is a viable alternative to the standard clients. It is available via Maven central as well.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;dependencies {
    compile group: 'io.searchbox',
        name: 'jest',
        version: '2.0.0'
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;JestClient&lt;/code&gt; is the central interface that allows to send requests to elasticsearch. It can be created using a factory.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(new HttpClientConfig
            .Builder(&quot;http://localhost:9200&quot;)
            .multiThreaded(true)
            .build());

JestClient client = factory.getObject();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As with the RestClient Jest doesn&amp;#39;t have any support for generating queries. You can either create them using String templating or reuse the elasticsearch builders (with the drawback of having to manage all dependencies again).&lt;/p&gt;

&lt;p&gt;A builder can be used to create the search request.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;String query = jsonStringThatMagicallyAppears;

Search search = new Search.Builder(query)
    .addIndex(&quot;library&quot;)
    .build();

SearchResult result = client.execute(search);
assertEquals(Integer.valueOf(1), result.getTotal());
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result can be processed by traversing the Gson object structure which can become rather complex.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;JsonObject jsonObject = result.getJsonObject();
JsonObject hitsObj = jsonObject.getAsJsonObject(&quot;hits&quot;);
JsonArray hits = hitsObj.getAsJsonArray(&quot;hits&quot;);
JsonObject hit = hits.get(0).getAsJsonObject();

// ... more boring code
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But that is not how you normally work with Jest. The good thing about Jest is that it directly supports indexing and search Java beans. For example we can have a representation of our dish documents.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;public class Dish {

    private String food;
    private List&amp;lt;String&amp;gt; tags;
    private Favorite favorite;

    @JestId
    private String id;

     // ... getters and setters
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This class can then be automatically populated from the search result.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;Dish dish = result.getFirstHit(Dish.class).source;

assertEquals(&quot;Roti Prata&quot;, dish.getFood());
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course the bean support can be used to index data as well.&lt;/p&gt;

&lt;p&gt;Jest can be a good alternative when accessing elasticsearch via http. It has a lot of useful functionality like the bean support when indexing and searching and a sniffing feature called node discovery. Unfortunately you have to create the search queries yourself but this is the case for the RestClient as well.&lt;/p&gt;

&lt;p&gt;Now that we have looked at three clients it is time to see an abstraction on a higher level.&lt;/p&gt;

&lt;h2&gt;Spring Data Elasticsearch&lt;/h2&gt;

&lt;p&gt;The family of Spring Data projects provides access to different data stores using a common programming model. It doesn&amp;#39;t try to provide an abstraction over all stores, the specialities of each store are still available. The most impressive feature is the dynamic repositories that allow you to define queries using an interface. Popular modules are Spring Data JPA for accessing relational databases and Spring Data MongoDB.&lt;/p&gt;

&lt;p&gt;Like all Spring modules the artifacts are available in Maven central.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;dependencies {
    compile group: 'org.springframework.data',
    name: 'spring-data-elasticsearch',
    version: '2.0.4.RELEASE'
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The documents to be indexed are represented as Java beans using custom annotations.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;@Document(indexName = &quot;spring_dish&quot;)
public class Dish {

    @Id
    private String id;
    private String food;
    private List&amp;lt;String&amp;gt; tags;
    private Favorite favorite;

     // more code

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Different annotations can be used to define how the document will be stored in elasticsearch. In this case we just define the index name to use when persisting the document and the property that is used for storing the id generated by elasticsearch.&lt;/p&gt;

&lt;p&gt;For accessing the documents one can define an interface typed to the dish class. There are different interfaces available for extension, &lt;code&gt;ElasticsearchCrudRepository&lt;/code&gt; provides generic index and search operations.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;public interface DishRepository 
  extends ElasticsearchCrudRepository&amp;lt;Dish, String&amp;gt; {

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The module provides a namespace for XML configuration.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;&amp;lt;elasticsearch:transport-client id=&quot;client&quot; /&amp;gt;

&amp;lt;bean name=&quot;elasticsearchTemplate&quot; 
  class=&quot;o.s.d.elasticsearch.core.ElasticsearchTemplate&quot;&amp;gt;
    &amp;lt;constructor-arg name=&quot;client&quot; ref=&quot;client&quot;/&amp;gt;
&amp;lt;/bean&amp;gt;

&amp;lt;elasticsearch:repositories 
  base-package=&quot;de.fhopf.elasticsearch.springdata&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;transport-client&lt;/code&gt; element instanciates a transport client, &lt;code&gt;ElasticsearchTemplate&lt;/code&gt; provides the common operations on elasticsearch. Finally, the &lt;code&gt;repositories&lt;/code&gt; element instructs Spring Data to scan for interfaces extending one of the Spring Data interface. It will automatically create instances for those.&lt;/p&gt;

&lt;p&gt;You can then have the repository wired in your application and use it for storing and finding instances of &lt;code&gt;Dish&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;Dish mie = new Dish();
mie.setId(&quot;hokkien-prawn-mie&quot;);
mie.setFood(&quot;Hokkien Prawn Mie&quot;);
mie.setTags(Arrays.asList(&quot;noodles&quot;, &quot;prawn&quot;));

repository.save(Arrays.asList(hokkienPrawnMie));

// one line ommited

Iterable&amp;lt;Dish&amp;gt; dishes = repository.findAll();

Dish dish = repository.findOne(&quot;hokkien-prawn-mie&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Retrieving documents by id is not very interesting for a search engine. To really query documents you can add more methods to your interface that follow a certain naming convention.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;public interface DishRepository 
  extends ElasticsearchCrudRepository&amp;lt;Dish, String&amp;gt; {

    List&amp;lt;Dish&amp;gt; findByFood(String food);

    List&amp;lt;Dish&amp;gt; findByTagsAndFavoriteLocation(String tag, String location);

    List&amp;lt;Dish&amp;gt; findByFavoritePriceLessThan(Double price);

    @Query(&quot;{\&quot;query\&quot;: {\&quot;match_all\&quot;: {}}}&quot;)
    List&amp;lt;Dish&amp;gt; customFindAll();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most of the methods start with &lt;code&gt;findBy&lt;/code&gt; followed by one or more properties. For example &lt;code&gt;findByFood&lt;/code&gt; will query the field &lt;code&gt;food&lt;/code&gt; with the given parameter. Structured queries are possible as well, in this case by adding &lt;code&gt;lessThan&lt;/code&gt;. This will return all dishes that have a lower price than the given one. The last method uses a different approach. It doesn&amp;#39;t follow a naming convention but uses a &lt;code&gt;Query&lt;/code&gt; annotation instead. Of course this query can contain placeholders for parameters as well.&lt;/p&gt;

&lt;p&gt;To wrap up, Spring Data Elasticsearch is an interesting abstraction on top of the standard client. It is somewhat tied to a certain elasticsearch version, the current release uses version 2.2. There are plans for making it compatible with 5.x but this may still take some time. There is a pull request that uses Jest for communication but it is unclear if and when this will be merged. Unfortunately there is not a lot activity in the project.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We have looked at three Java clients and the higher level abstraction Spring Data Elasticsearch. Each of those has its pros and cons and there is no advice to use one in all cases. The transport client has full API support but is tied to the elasticsearch dependency. The RestClient is the future and will one day supersede the transport client. Feature wise it is currently very low level. Jest has a richer API but is developed externally and the company behind it doesn&amp;#39;t seem to exist anymore though there is activity by the commiters in the project. Spring Data Elasticsearch on the other hand is better suited for developers using Spring Data already and don&amp;#39;t want to get in contact with the elasticsearch API directly. It is currently tied to a version of the standard client, development activity is rather low.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Book Review: Relevant Search</title>
   <link href="http://blog.florian-hopf.de/2016/10/book-review-relevant-search.html"/>
   <updated>2016-10-20T07:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/10/book-review-relevant-search</id>
   <content type="html">&lt;p&gt;Relevancy, the notion that some results are better than others is one of the key factors that distinguishes search engines from most other databases. Additionaly it is a task that can sometimes seem like magic and is difficult to get right. Applications like Google have set the bar for how a search engine is expected to work. The relevant results should all be on the top positions. As the saying goes, if you want to make sure a secret stays a secret put it on page 3 of a Google search result page.&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.manning.com/books/relevant-search&quot; style=&quot;clear:left; float:left;margin-right:1em; margin-bottom:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; width=&quot;255&quot; src=&quot;/files/relevant-search.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Doug Turnbull and John Berryman have written &lt;a href=&quot;https://www.manning.com/books/relevant-search&quot;&gt;a book about all aspects that are related to relevancy&lt;/a&gt;. You will learn how the inverted index works, about different kinds of queries and the way they influence the score of the result documents. You will see how you can use boost or special queries to influence the result ordering and about different ways to help the user find the things they are looking for. &lt;/p&gt;

&lt;p&gt;For most parts the book uses one coherent example, the search for movies. This is very well suited as it is a mixture of structured and unstructured data. All the examples in the book are using Elasticsearch but there is also an appendix that shows how to do similar things with Solr.&lt;/p&gt;

&lt;p&gt;When starting with the book I thought I had a basic introduction to search engines in my hand. But I was wrong - both authors obviously have lots of experience with search relevancy tuning (no wonder they are connected to the development of tools like &lt;a href=&quot;http://splainer.io/&quot;&gt;Splainer&lt;/a&gt; and &lt;a href=&quot;http://quepid.com/&quot;&gt;Quepid&lt;/a&gt;). The book is different from a lot of other books on search technologies in that it doesn&amp;#39;t describe all the features of a certain search engine but shows how to use them to build business applications. &lt;/p&gt;

&lt;p&gt;Event though I am intensively working with search engines myself I learned a lot while reading the book. Some of the tactics are things that are widely done when building applications based on search engines but the authors manage to name them explicitly and build a structured approach. Finally, besides being very informative the book is an easy read that contains lots of jokes. If you are doing something with search engines you are well advised to read it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Simple Way to Index Java Beans in Elasticsearch</title>
   <link href="http://blog.florian-hopf.de/2016/07/index-java-beans-elasticsearch.html"/>
   <updated>2016-07-01T07:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/07/index-java-beans-elasticsearch</id>
   <content type="html">&lt;p&gt;When it comes to data stores Java programmers are used to working with Java beans that are magically persisted. Solutions like &lt;a href=&quot;http://hibernate.org&quot;&gt;Hibernate&lt;/a&gt; and the &lt;a href=&quot;https://en.wikipedia.org/wiki/Java_Persistence_API&quot;&gt;JPA specification&lt;/a&gt; for relational data stores or &lt;a href=&quot;https://mongodb.github.io/morphia/&quot;&gt;Morphia&lt;/a&gt; and &lt;a href=&quot;http://projects.spring.io/spring-data-mongodb/&quot;&gt;Spring Data MongoDB&lt;/a&gt; are popular examples.&lt;/p&gt;

&lt;p&gt;Developers working with Elasticsearch sometimes have the same desire - pass a Java bean and have it indexed automatically. There is an implementation of &lt;a href=&quot;http://projects.spring.io/spring-data-elasticsearch/&quot;&gt;Spring Data for Elasticsearch&lt;/a&gt; available but it might be overhead for you or not be supported by your version of Elasticsearch. And there&amp;#39;s &lt;a href=&quot;https://github.com/searchbox-io/Jest/tree/master/jest&quot;&gt;Jest&lt;/a&gt; which uses the HTTP API that supports storing Java Beans directly.&lt;/p&gt;

&lt;p&gt;If you want to do the same using the standard Java client for Elasticsearch there is no direct support for that but it can be implemented by hand easily. &lt;/p&gt;

&lt;p&gt;Suppose you want to persist the following simple object structure that represents a book.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;Publisher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Publisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setCountry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UK&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Packt&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setTitle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Learning Spring Boot&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAuthors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Arrays&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;asList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Greg L. Turnquist&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Often it happens that we are thinking so hard about one way to solve a problem that we can&amp;#39;t see the easier way. We don&amp;#39;t need a special framework for Elasticsearch. Elastcsearch will happily store most JSON structures for you. And fortunately creating JSON documents from Java objects is a solved problem using Libraries like &lt;a href=&quot;https://github.com/FasterXML/jackson&quot;&gt;Jackson&lt;/a&gt; or &lt;a href=&quot;https://github.com/google/gson&quot;&gt;GSON&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We can simply add a dependency, in this case to &lt;a href=&quot;https://github.com/FasterXML/jackson-databind&quot;&gt;jackson-databind&lt;/a&gt;, to the project if it&amp;#39;s not already there and instanciate an ObjectMapper.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;ObjectMapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObjectMapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you&amp;#39;re using Spring Boot you will normally even be able to just @Autowire the ObjectMapper. The ObjectMapper can then be used to create a JSON representation of the object.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;writeValueAsString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will result in a string similar to this one.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Learning Spring Boot&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;authors&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Greg L. Turnquist&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;publisher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Packt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;country&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;UK&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can then index the result using the Elasticsearch client interface.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;IndexResponse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;prepareIndex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;actionGet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When retrieving the document you can create Java objects again using the readValue method.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;GetResponse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getResponse&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;prepareGet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;book&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;actionGet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSourceAsString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persistedBook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;readValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Packt&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persistedBook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getPublisher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Or even better: Maybe you don&amp;#39;t even need to create a Java object again? When you&amp;#39;re only displaying the result in a template maybe it&amp;#39;s enough to just pass in a Map of the resulting document?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceAsMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;getResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSourceAsMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Sometimes we are looking for complicated solutions when we don&amp;#39;t even need them. As Elasticsearch uses JSON everywhere it is very easy to use common libraries for serialization, be it in Java or in other languages.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>On Writing a Book</title>
   <link href="http://blog.florian-hopf.de/2016/06/on-writing-a-book.html"/>
   <updated>2016-06-17T07:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/06/on-writing-a-book</id>
   <content type="html">&lt;p&gt;Last December I was holding the final printed version of my &lt;a href=&quot;http://elasticsearch-buch.de&quot;&gt;German book on Elasticsearch&lt;/a&gt; in my hands. It's a good feeling and though there were stressful times I don't regret writing it. In this very long post I'd like to talk about what it's like to write and publish a book the traditional way.&lt;/p&gt;

&lt;h2&gt;Signing the contract&lt;/h2&gt;

&lt;p&gt;I first got approached by &lt;a href=&quot;http://dpunkt.de&quot;&gt;dpunkt.verlag&lt;/a&gt; in June 2013. &lt;a href=&quot;http://n-k.de&quot;&gt;Niko Köbler&lt;/a&gt; talked to an editor of the publisher at a conference, they somehow got to Elasticsearch and he recommended me as a potential author. The first call I had with the editor was very informal &amp;ndash; we talked about search technology and the role of Elasticsearch. They were thinking about doing a book about it in the future but there were no concrete plans. We agreed to wait for half a year and then see again.&lt;/p&gt;

&lt;p&gt;I didn&amp;#39;t really think that something would come out of it at all but in the end of March 2014 the editor contacted me again, telling me that they wanted to do a book on Elasticsearch and if I would be interested.&lt;/p&gt;

&lt;p&gt;I always thought that writing a book is a good thing. Not that I really worked on going that route but I somehow admired people who had done it. So I didn&amp;#39;t have to think a lot &amp;ndash; I wanted to do it and agreed.&lt;/p&gt;

&lt;p&gt;Even before signing a contract I had to create an outline of the book, i.e. create a table of contents with the headlines for the different chapters and sections in the book. This is more difficult than it might sound, even when you think you know what you want to write about. You need to make sure that all the necessary topics are covered and that it&amp;#39;s a coherent story. &lt;/p&gt;

&lt;p&gt;This outline is then sent to several reviewers who provide feedback for the author (and very likely also assure the publisher that the author is the right person for the job). After incorporating some feedback in the outline I had to put milestone dates on the chapters. That also means defining the order the chapters will be written in, which normally is not the order they are read in (example: the first chapter in the book is the last chapter I wrote). I already expected the writing process to be a huge undertaking, taking longer than I would think. I planned on finishing writing the book in around eight months, having it released in about one year. I signed the contract in July 2014 at Java Forum Stuttgart.&lt;/p&gt;

&lt;h2&gt;Tools&lt;/h2&gt;

&lt;p&gt;I had the choice of technology for writing the book &amp;ndash; Word, Open Office documents or LaTex. I chose LaTex that I already knew from writing my thesis and I suspected would be less problematic when it comes to formatting issues. Later I sometimes regretted the choice as I had to struggle a lot setting the system up. Only later we found out that I didn&amp;#39;t receive all the necessary files, so these problems could have been prevented.&lt;/p&gt;

&lt;p&gt;Nearly all of my writing was done directly in vim. I had a notebook but didn&amp;#39;t do a lot of writing on paper (but I am writing the first version of the post you are just reading on paper). I put all the assets under version control in Git, also pushing them to a remote server for backup and sometimes copying all of it on USB sticks. You really don&amp;#39;t want to start again from the beginning after that much work :).&lt;/p&gt;

&lt;p&gt;I struggled a bit with the choice of tool for images. I started with some placeholder images I created using &lt;a href=&quot;http://www.yworks.com/products/yed&quot;&gt;yEd&lt;/a&gt;, experimented with some other tools, and finally went back to yEd again. There might be more beautiful diagrams than the ones in the book but I am quite happy with the result.&lt;/p&gt;

&lt;h2&gt;The Writing Process&lt;/h2&gt;

&lt;p&gt;Writing happens on a chapter by chapter basis according to the milestone plan I created with the outline. If you&amp;#39;re lucky like me you can find people who are willing to review the chapters for you. Otherwise the publisher will have people for reviewing the book. I contacted some people for help and saved the publishers reviewers for the final version of the book. I am especially thankful for the help of &lt;a href=&quot;https://twitter.com/tokraft&quot;&gt;Tobias Kraft&lt;/a&gt; of &lt;a href=&quot;http://exensio.de&quot;&gt;Exensio&lt;/a&gt; who reviewed each and every chapter in detail. Thanks for your work Tobias!&lt;/p&gt;

&lt;p&gt;I started writing with the second chapter that I planned to be one coherent example on how to use Elasticsearch, how analyzing and search works and so on. I was already well prepared for writing as during the time I started to work on the book I was publishing blog posts regularly, nearly on a weekly basis. I planned on establishing a writing habit, due to my work as a Freelancer I was able to reduce client projects to four days a week and have one day I can dedicate to writing alone. But even though I planned to do so it was difficult to execute. Half a year is a very long time span and you tend to have more urgent things to do &amp;ndash; &amp;quot;I will catch up on writing later on&amp;quot; &amp;ndash; except you don&amp;#39;t.&lt;/p&gt;

&lt;p&gt;When I finished the second chapter it had taken a lot longer than expected and it grew very long. Even I knew that this was not the quality to be published in a book. And the feedback of my editor and the reviewers was the same. Though they could see where I wanted to go my writing wasn&amp;#39;t good yet. The sentences were too long, you could notice the different times I had written the parts and all in all the chapter was far too long for an initial example. Back to the writing desk.&lt;/p&gt;

&lt;p&gt;I was already late and had to redo everything. But after a while it got better: I extracted two more chapters on topics I didn&amp;#39;t originally plan to write about in detail. But still I had to take off too much time from writing for doing other things. Months passed and I noticed I wouldn&amp;#39;t be able to make the deadline. In January 2015 I re-planned the dates for book delivery, extending the deadlines for about two months. &lt;/p&gt;

&lt;p&gt;Writing went well afterwards, I managed to get into some kind of flow more regularly but still I was taking too long. In March I extended for another month, planning on finishing the book in the end of April. In the end of March I noticed that even this plan would not work out and extended for another two months, finishing writing in the end of June 2015. I managed to meet this goal.&lt;/p&gt;

&lt;p&gt;I logged all the time for writing in an excel sheet in 15 minute blocks. I became so obsessed with collecting the numbers that I also didn&amp;#39;t work on the book if I had only five or ten minutes because then I couldn&amp;#39;t log the time correctly. &lt;/p&gt;

&lt;p&gt;Writing happened either in the morning before doing customer work or on dedicated days I spent at the library. It&amp;#39;s good for me to have a dedicated work environment. Most of the days I felt really exhausted after working around six hours on the book. Writing is more challenging than normal programming work.&lt;/p&gt;

&lt;h2&gt;The Editorial Process&lt;/h2&gt;

&lt;p&gt;After I was done with writing a pdf was sent to some reviewers who had around three weeks to send their feedback. The result was really helpful with useful comments and suggestions. I incorporated some feedback for the final version, being extra careful to not add any errors because no technical reviewer would see those edits anymore. &lt;/p&gt;

&lt;p&gt;This was also the time I did the index for the book, using the headlines as a guidance. Having a bad index can be really annoying for a technical book. This also took longer than you might think. I also had to redo the bibliography because I used the wrong notation. This basically meant typing all of the list again. I thought about some automation but in the end it was done by hand in a few hours.&lt;/p&gt;

&lt;p&gt;After doing the edits I sent the manuscript to the publisher for final proofreading.&lt;/p&gt;

&lt;p&gt;It was a bit of a surprise for me that the corrections arrived via mail and on paper. But it&amp;#39;s the same with me &amp;ndash; reading and correcting on paper works better.&lt;/p&gt;

&lt;p&gt;After having added the corrections to the manuscript the book went off for typesetting which roughly took another two weeks. Afterwards I had to change some things regarding the code formatting and redo some of the images. The final version then went to print in mid November, the first big relief for me. &lt;/p&gt;

&lt;h2&gt;Holding the Book in the Hand&lt;/h2&gt;

&lt;p&gt;Before extending the deadlines I expected the book to be published in July 2015. When it was added to Amazon the initial release date stated September 2015, later October, then November and finally December 2015. I had a longer holiday booked for Christmas and I was really eager to hold the book in my hands before leaving.&lt;/p&gt;

&lt;p&gt;Fortunately the first copy of the book was sent to me December 11 2015 and a few days later a box with all my author copies arrived.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;de&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Happy that my &lt;a href=&quot;https://twitter.com/hashtag/Elasticsearch?src=hash&quot;&gt;#Elasticsearch&lt;/a&gt; book is now available. A package full of past work :-) &lt;a href=&quot;https://t.co/a4wWJpvmc0&quot;&gt;https://t.co/a4wWJpvmc0&lt;/a&gt; &lt;a href=&quot;https://t.co/zhM1llaUbx&quot;&gt;pic.twitter.com/zhM1llaUbx&lt;/a&gt;&lt;/p&gt;&amp;mdash; Florian Hopf (@fhopf) &lt;a href=&quot;https://twitter.com/fhopf/status/676814847762423808&quot;&gt;15. Dezember 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;I can tell you, it&amp;#39;s an excellent feeling holding the book in your hands after a year and a half of often stressful times.&lt;/p&gt;

&lt;h2&gt;The Website&lt;/h2&gt;

&lt;p&gt;Quite early I decided that it&amp;#39;s a good idea to have a dedicated website for the book. I got me the domain &lt;a href=&quot;http://elasticsearch-buch.de&quot;&gt;elasticsearch-buch.de&lt;/a&gt; for marketing and for putting stuff on it that didn&amp;#39;t make it in the book. Fortunately until today I didn&amp;#39;t have to add an errata section :).&lt;/p&gt;

&lt;p&gt;I created the website in the end of November 2015 using the static site generator &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; and a predefined template. I put up some articles and marketing texts and a list of all the resources in the book as clickable links. Even after the book was released I hadn&amp;#39;t added all the content I was referring to in the book. There were some quite stressful last minute publish processes before leaving for a longer holiday.&lt;/p&gt;

&lt;p&gt;After the release I also had the chance to publish a &lt;a href=&quot;https://www.elastic.co/de/blog/elasticsearch-ein-praktischer-einstieg&quot;&gt;guest blog post about the book&lt;/a&gt; on the German elastic blog.&lt;/p&gt;

&lt;h2&gt;Compensation&lt;/h2&gt;

&lt;p&gt;It&amp;#39;s no secret that this book won&amp;#39;t make me rich. I don&amp;#39;t know how many copies will be sold but in the end I expect the hourly rate for the writing to be in the low one digit € range. You might not be surprised that my consulting rates are a bit higher than that, but of course the book is good marketing for services. I received less requests for work than expected afterwards but having written the book I am more confident to request higher rates because I know the topic in depth. &lt;/p&gt;

&lt;p&gt;But it&amp;#39;s not only a money thing. I learned a lot while writing the book, going into far more detail I would have done otherwise, making sure that I really understood all the functionality I was describing.&lt;/p&gt;

&lt;p&gt;Finally having written a book just feels good.&lt;/p&gt;

&lt;h2&gt;Statistics&lt;/h2&gt;

&lt;p&gt;Finally some numbers on the writing process.&lt;/p&gt;

&lt;table&gt;
&lt;tr&gt;&lt;td&gt;Number of commits in repo&lt;/td&gt;&lt;td&gt;266&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;First commit with chapter content&lt;/td&gt;&lt;td&gt;16.06.2014&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Last commit before sending the book for publishing&lt;/td&gt;&lt;td&gt;13.11.2015&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Number of time entries logged&lt;/td&gt;&lt;td&gt;162&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Number of hours logged&lt;/td&gt;&lt;td&gt;342&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Character numbers of all tex files&lt;/td&gt;&lt;td&gt;495701&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Number of resources in bibliography&lt;/td&gt;&lt;td&gt;199&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;I was a bit surprised to see that I had &lt;em&gt;only&lt;/em&gt; logged 342 hours. So I could have done the book in around two months working 40 hours a week. Except that this wouldn&amp;#39;t have been possible &amp;ndash; there was a lot of conscious and unconscious thinking involved when I wasn&amp;#39;t writing. Besides that it is nearly impossible for me to write for more than six hours a day.&lt;/p&gt;

&lt;h2&gt;On Versions&lt;/h2&gt;

&lt;p&gt;Elasticsearch is a very active project with lots of features added for new releases. I started writing the book using Elasticsearch 1.3.1, later switching to 1.4.x and then to the final version of the book 1.6. I was quite dedicated to ensure there are no mistakes in the example requests so after switching versions I had to make sure that all the requests were still working. This also means making sure the output of queries and the log output are still the same.&lt;/p&gt;

&lt;p&gt;During the time of writing the rivers that were used to pull data in Elasticsearch were deprecated. I had already described the Twitter river to gather data from Twitter for use in the aggregations chapter. As switching this to another mechanism very likely meant that the document structure changed and I would have to redo all the aggregation examples I decided to stick with the river and explain &lt;a href=&quot;http://elasticsearch-buch.de/2015/12/03/logstash-twitter.html&quot;&gt;how the same can be done using Logstash in a separate article on the website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another thing that happened in February 2015 was the rebranding of the company behind Elasticsearch to elastic. This meant that I had to redo all the links and all mentions of the company. This was more work than I would have thought in advance.&lt;/p&gt;

&lt;p&gt;Two rather bad things happened with regards to versioning. In the chapter on centralized logging I describe Graylog as an alternative to the ELK stack. I used version 0.9.2 but in February 2015 Graylog 1.0 was released. As I had the chapter finished already including lots of screenshots and didn&amp;#39;t have any time to spare I decided to stay with the version, stating it explicitly and just making sure that I didn&amp;#39;t describe features that were gone in Graylog 1.0.&lt;/p&gt;

&lt;p&gt;Even worse, after finishing the manuscript Elasticsearch 2.0 was released. It sounds like a big deal but lots of the changes were under the hood and it didn&amp;#39;t change that much for the user. I wasn&amp;#39;t able to redo everything for 2.0 but wanted to make sure that the book was also valid for the new version. I published &lt;a href=&quot;http://elasticsearch-buch.de/2015/12/16/elasticsearch-2.html&quot;&gt;a blog post&lt;/a&gt; on the website for the book describing the features and changes.&lt;/p&gt;

&lt;p&gt;Generally I think it is very important to clearly state what version of software components you are using for writing the book. Otherwise readers might be confused when other versions don&amp;#39;t work as described.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Writing a book can be extremely stressful but also is a very rewarding experience. Having a publisher with an editor means that you do have competent help when it comes to language and style (some publishers might be different and not focused on quality that much). The process of writing the book will never be paid from the sales alone. But it can be useful for marketing and it&amp;#39;s a great way to learn about something you care about in depth.&lt;/p&gt;

&lt;p&gt;The process is a classic example of &lt;a href=&quot;https://en.wikipedia.org/wiki/Hofstadter&amp;#x27;s_law&quot;&gt;Hofstadters law&lt;/a&gt;: It always takes longer than you expect, even when you take into account Hofstadter&amp;#39;s Law. I wasn&amp;#39;t able to spend that much time on writing as I was planning to, additionally reviewing and incorporating feedback took longer than I would have expected. Nevertheless I don&amp;#39;t regret it &amp;ndash; it was a good experience.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stringify Everything in Elasticsearch</title>
   <link href="http://blog.florian-hopf.de/2016/05/stringify-everything-elasticsearch.html"/>
   <updated>2016-05-27T06:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/05/stringify-everything-elasticsearch</id>
   <content type="html">&lt;p&gt;A while ago I was working on a prototype to search larger structured documents using Elasticsearch. We were only interested to make the text searchable, with an option to search all the text and some seperate fields. Elasticsearch is of course a perfect solution for this with the &lt;code&gt;_all&lt;/code&gt; field and the possiblility to search single or multiple fields.&lt;/p&gt;

&lt;p&gt;The documents we had to make searchable were rather complex, consisting of hundreds of fields with different data types, special identifiers and numeric and string values. We had everything exported in JSON documents but unfortunately the data types were mixed and changing from document to document. Indexing two example documents might look like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;POST /example/doc
{
    &quot;my-id&quot;: 1,
    &quot;my-tag&quot;: &quot;one&quot;,
    &quot;my-flag&quot;: true
}

POST /example/doc
{
    &quot;my-id&quot;: &quot;1b&quot;,
    &quot;my-tag&quot;: &quot;one b&quot;,
    &quot;my-flag&quot;: &quot;enabled&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What happens when these documents are indexed? Elasticsearch will try to guess the field type by its value. For the first document &lt;code&gt;my-id&lt;/code&gt; clearly is a numeric value and &lt;code&gt;my-flag&lt;/code&gt; a boolean. But for the second document the field types change to string values that of course can&amp;#39;t be indexed in a numeric or boolean field. So indexing will fail for the second document. What can be done? &lt;/p&gt;

&lt;p&gt;Of course the best way would have been to create the JSON documents correctly in the first place but this would have been rather complex because of the environment we were working in. As we were working on a prototype the quicker solution was to create a mapping for Elasticsearch that treats the values as strings. But creating a dedicated mapping would have been too complex &amp;ndash; remember there were hundres of fields that we would have to check and configure. As we were ok with just treating all the fields in the documents as string values the solution was to add a &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html&quot;&gt;dynamic template&lt;/a&gt; to our index that then maps all fields to string.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-&quot; data-lang=&quot;&quot;&gt;DELETE /example

PUT /example

PUT /example/doc/_mapping
{
   &quot;doc&quot;: {
      &quot;dynamic_templates&quot;: [
         {
            &quot;all_strings&quot;: {
               &quot;match&quot;: &quot;*&quot;,
               &quot;mapping&quot;: {
                  &quot;type&quot;: &quot;string&quot;,
                  &quot;analyzer&quot;: &quot;standard&quot;
               }
            }
         }
      ]
   }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, when indexing the documents again, Elasticsearch will consider the dynamic template &lt;code&gt;all_strings&lt;/code&gt;. By adding &lt;code&gt;*&lt;/code&gt; for &lt;code&gt;match&lt;/code&gt; this will be enabled for all fields. Each field will then automatically be configured as a string value, all documents can be indexed and searched afterwards.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Blog Relaunch</title>
   <link href="http://blog.florian-hopf.de/2016/05/blog-relaunch.html"/>
   <updated>2016-05-16T14:16:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/05/blog-relaunch</id>
   <content type="html">&lt;p&gt;I started a blog on blogspot in 2009, just to have something to put my thoughts on. In the beginning I did not blog regularly with 17 posts from 2009 to 2011 but wrote my most popular post so far when it comes to total visits on &lt;a href=&quot;http://blog.florian-hopf.de/2012/08/getting-rid-of-synchronized-using-akka.html&quot;&gt;using Akka from Java&lt;/a&gt;. When starting as an independent developer in 2012 it was clear for me that I wanted to use blogging for my marketing because it's a great way for me: When writing posts I can learn a lot about different topics I am interested in.&lt;/p&gt;

&lt;p&gt;As I wanted to have the blog linked to my name I configured blogspot to use blog.florian-hopf.de instead of the old fhopf.blogspot.com. At the same time I introduced some pages about my work, articles I had written and talks I had given. I generated some static files using &lt;a href=&quot;http://nanoc.ws/&quot;&gt;Nanoc&lt;/a&gt; with a very simple bootstrap based theme. Those files were then being delivered using www.florian-hopf.de. To give the impression of a coherent website I added the same template to blogspot so it might have happened that you didn&amp;#39;t even notice that two systems were involved.&lt;/p&gt;

&lt;p&gt;After starting as an independent developer I also increased my blogging frequency with publishing weekly for several months. This lead to 13 posts in 2012 (I started freelancing in May), 25 posts in 2013 and 29 posts in 2014. As I then started writing my &lt;a href=&quot;http://elasticsearch-buch.de&quot;&gt;book on Elasticsearch&lt;/a&gt; there were nearly no posts in 2015 and 2016 but I am planning to write more regularly soon.&lt;/p&gt;

&lt;p&gt;So far the combination of blogger and static website worked for me but it had some drawbacks. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Though I got Nanoc somehow working I wasn&amp;#39;t really convinced it&amp;#39;s the best tool for the job.&lt;/li&gt;
&lt;li&gt;The fonts of the template were far too small, especially for mobile devices.&lt;/li&gt;
&lt;li&gt;I always had to keep the templates in sync. When changing something on the website (e.g. a new navigation item) I had to add it to the blogger template as well.&lt;br&gt;&lt;/li&gt;
&lt;li&gt;Finally the most important drawback: I am writing most blog post in text files first and version them using Git. Going through the blogger web interface felt like a totally unnecessary step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fortunately finally I found the time to do something against it and you are looking at it right now. This site is completely generated using &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;, the template is based on the simple responsive &lt;a href=&quot;https://github.com/poole/hyde&quot;&gt;Hyde&lt;/a&gt;. Jekyll provides an &lt;a href=&quot;https://import.jekyllrb.com/docs/blogger/&quot;&gt;easy way to migrate your blogger content&lt;/a&gt;. The comments are now on &lt;a href=&quot;https://disqus.com&quot;&gt;Disqus&lt;/a&gt; and I hope that all old comments are still accessible. RSS is still delivered using Feeburner that now points to my server. The blog is still running on the old subdomain but all of the content is now delivered using my server. &lt;/p&gt;

&lt;p&gt;With the new template I hope my posts can be read more easily, especially on mobile devices. If you notice something weird I would be really happy if you let me know. I hope you will enjoy some of the posts I will be writing in the future.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Learning Lucene</title>
   <link href="http://blog.florian-hopf.de/2016/04/learning-lucene.html"/>
   <updated>2016-04-05T14:16:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/04/learning-lucene</id>
   <content type="html">&lt;p&gt;I am currently working with a team starting a new project based on Lucene. While most of the time I would argue on using either Solr or Elasticsearch instead of plain Lucene it was a conscious decision. In this post I am compiling some sources for learning Lucene – I hope you will find them helpful or you can hint what sources I missed.&lt;/p&gt;&lt;h4&gt;Project documentation&lt;/h4&gt;&lt;p&gt;The first choice of course is the excellent &lt;a href=&quot;http://lucene.apache.org/core/5_5_0/index.html&quot;&gt;project documentation&lt;/a&gt;. It contains the Javadoc for all the modules (&lt;a href=&quot;http://lucene.apache.org/core/5_5_0/core/index.html&quot;&gt;core&lt;/a&gt;, &lt;a href=&quot;http://lucene.apache.org/core/5_5_0/analyzers-common/index.html&quot;&gt;analyzers-common&lt;/a&gt; and &lt;a href=&quot;http://lucene.apache.org/core/5_5_0/queryparser/index.html&quot;&gt;queryparser&lt;/a&gt; being the most important ones) that also contains further documentation, for example an &lt;a href=&quot;http://lucene.apache.org/core/5_5_0/demo/overview-summary.html#overview_description&quot;&gt;explanation of a simple demo app&lt;/a&gt; and helpful introductions to &lt;a href=&quot;http://lucene.apache.org/core/5_5_0/core/org/apache/lucene/analysis/package-summary.html#package_description&quot;&gt;analysis&lt;/a&gt; and &lt;a href=&quot;http://lucene.apache.org/core/5_5_0/core/org/apache/lucene/search/package-summary.html#package_description&quot;&gt;querying and scoring&lt;/a&gt;. You might also be interested in the &lt;a href=&quot;http://lucene.apache.org/core/5_5_0/core/org/apache/lucene/codecs/lucene54/package-summary.html#package_description&quot;&gt;standard index file formats&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Besides the documentation that comes with the releases there is also lots of information in the &lt;a href=&quot;http://wiki.apache.org/lucene-java/FrontPage?action=show&amp;amp;redirect=FrontPageEN&quot;&gt;project wiki&lt;/a&gt; but you need to know what you are looking for. You can also join the &lt;a href=&quot;http://lucene.apache.org/core/discussion.html&quot;&gt;mailing lists&lt;/a&gt; to learn about what other users are doing.&lt;/p&gt;&lt;p&gt;When looking at analyzer components the &lt;a href=&quot;http://www.solr-start.com/&quot;&gt;Solr Start&lt;/a&gt; website can be useful. Though dedicated to Solr the &lt;a href=&quot;http://www.solr-start.com/info/analyzers/&quot;&gt;list of analyzer components&lt;/a&gt; can be useful to determine analyzers for Lucene as well. It also contains a &lt;a href=&quot;http://www.solr-start.com/javadoc/solr-lucene/index.html&quot;&gt;searchable version of the Javadocs&lt;/a&gt;.&lt;/p&gt;&lt;h4&gt;Books&lt;/h4&gt;&lt;p&gt;The classic book about the topic is &lt;a href=&quot;https://www.manning.com/books/lucene-in-action-second-edition&quot;&gt;Lucene in Action&lt;/a&gt;. On over 500 pages it explains all the underlying concepts in detail. Unfortunately some of the information is outdated and lots of the code examples won't work anymore. Also the newer concepts are not included. Still it's the recommended piece on learning Lucene.&lt;/p&gt;&lt;p&gt;Anonther book I've read is &lt;a href=&quot;https://www.packtpub.com/big-data-and-business-intelligence/lucene-4-cookbook&quot;&gt;Lucene 4 Cookbook&lt;/a&gt; published at Packt. It contains more current examples but is not suited well for learning the basics. Additionally it felt to me as if no editor worked on this book, there are lots of repetitions, typos and broken sentences. (I am making lots of grammar mistakes myself when blogging - but I am expecting more from a published book.)&lt;/p&gt;&lt;p&gt;You can also learn a lot about different aspects of Lucene by reading a book on one of the search servers based on it. I can recommend &lt;a href=&quot;https://www.manning.com/books/elasticsearch-in-action&quot;&gt;Elasticsearch in Action&lt;/a&gt;, &lt;a href=&quot;https://www.manning.com/books/solr-in-action&quot;&gt;Solr in Action&lt;/a&gt; and &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/guide/master/index.html&quot;&gt;Elasticsearch – The definitive Guide&lt;/a&gt;. (If you can read German I am of course inviting you to read &lt;a href=&quot;http://elasticsearch-buch.de/&quot;&gt;my book on Elasticsearch&lt;/a&gt;.)&lt;/p&gt;&lt;h4&gt;Blogs, Conferences and Videos&lt;/h4&gt;&lt;p&gt;There are countless blog posts on Lucene, a very good introduction is &lt;a href=&quot;http://blog.parsely.com/post/1691/lucene/&quot;&gt;Lucene: The Good Parts by Andrew Montalenti&lt;/a&gt;. Some blogs publish regular pieces on Lucene, recommended ones are by &lt;a href=&quot;http://blog.mikemccandless.com/&quot;&gt;Mike McCandless&lt;/a&gt; (who now mostly blogs on the &lt;a href=&quot;https://www.elastic.co/blog/author/michael-mccandless&quot;&gt;elastic Blog&lt;/a&gt;), &lt;a href=&quot;http://opensourceconnections.com/blog/&quot;&gt;OpenSource Connections&lt;/a&gt;, &lt;a href=&quot;http://www.flax.co.uk/blog/&quot;&gt;Flax&lt;/a&gt; and &lt;a href=&quot;http://blog.thetaphi.de/&quot;&gt;Uwe Schindler&lt;/a&gt;. There is a lot of content about Lucene on the &lt;a href=&quot;https://www.elastic.co/blog/&quot;&gt;elastic Blog&lt;/a&gt;, if you want to hear about current development I can recommend the &quot;This week in Elasticsearch and Apache Lucene&quot; series. There are also some interesting posts on the &lt;a href=&quot;http://lucidworks.com/blog/&quot;&gt;Lucidworks Blog&lt;/a&gt; and I am sure there are lots of other blogs I forgot to mention here.&lt;/p&gt;&lt;p&gt;Lucene is a regular topic on two larger conferences: &lt;a href=&quot;http://lucenerevolution.org/&quot;&gt;Lucene/Solr Revolution&lt;/a&gt; and &lt;a href=&quot;http://berlinbuzzwords.de/&quot;&gt;Berlin Buzzwords&lt;/a&gt;. You can find lots of video recordings of the past events on their website.&lt;/p&gt;&lt;h4&gt;Sources&lt;/h4&gt;&lt;p&gt;Finally, the project is open source so you can learn a lot about it by reading the source code of either the library or the tests.&lt;/p&gt;&lt;p&gt;Another option is to look at applications using it, either Solr and Elasticsearch. Of course you need to find your way around the sources of the project but sometimes this isn't too hard. One example for Elasticsearch: If you would like to learn about how the common multi_match-Query is implemented in Lucene you will easily find the class &lt;a href=&quot;https://github.com/elastic/elasticsearch/blob/master/core/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java&quot;&gt;MultiMatchQuery&lt;/a&gt; that creates the Lucene queries.&lt;/p&gt;&lt;h4&gt;What did I miss?&lt;/h4&gt;&lt;p&gt;I hope there is something useful for you in this post. I am sure I missed lots of great resources for learning Lucene. If you would like to add one let me know in the comments or on &lt;a href=&quot;https://twitter.com/fhopf&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Logging Requests to Elasticsearch</title>
   <link href="http://blog.florian-hopf.de/2016/03/logging-requests-to-elasticsearch.html"/>
   <updated>2016-03-23T14:09:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2016/03/logging-requests-to-elasticsearch</id>
   <content type="html">&lt;p&gt;This is something I wanted to write down for years but never got down to completing the post. It can help you a lot with certain Elasticsearch setups by answering two questions using the &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-slowlog.html&quot;&gt;slow log&lt;/a&gt;.&lt;/p&gt;   &lt;ul&gt;&lt;li&gt;Is my application talking to Elasticsearch?&lt;/li&gt;&lt;li&gt;What kind of queries are being built by my application?&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;A while ago I helped a colleague on one of my current projects to debug some problems with Elasticsearch integrated into proprietary software. He was not sure if there are any requests arriving at Elasticsearch and what those look like. We activated the slow log for Elasticsearch, which not only can be used to log the slow queries but also to enable debugging for any queries that reach Elasticsearch.&lt;/p&gt; &lt;p&gt;The slow log, as the name suggests, is there to log slow requests. As slow is a subjective term you can define thresholds that need to be passed. For example you can define that any queries slower than 50ms are logged in the debug level but any queries that take longer than 500ms in the warn level.&lt;/p&gt; &lt;p&gt;Slow queries can be configured for both phases of the query execution: query and fetch. In the query phase only the ids of the documents are retrieved in the form of a search result list. The fetch phase is where the result documents are retrieved.&lt;/p&gt; &lt;p&gt;Besides the slow query log there is also the slow index log which can be used in the same way but measures the time for indexing.&lt;/p&gt; &lt;p&gt;Both of these settings are index settings. That means they are configured for each index and can therefore be different across indices.&lt;/p&gt; &lt;h4&gt;Instance Settings&lt;/h4&gt; &lt;p&gt;There are multiple places where you can configure index settings. The first is &lt;code&gt;config/elasticsearch.yml&lt;/code&gt; that contains the configuration of the instance. For older versions of Elasticsearch it already contains the lines that are commented out, in newer versions you need to include them yourself. If you want to log all requests at debug level you can just add the following lines and set a threshold of 0s.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;index.search.slowlog.threshold.query.debug: 0s&lt;br /&gt;index.search.slowlog.threshold.fetch.debug: 0s&lt;br /&gt;index.indexing.slowlog.threshold.index.debug: 0s&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You need to reboot the instance so that the settings are activated. Any indexing and search requests will now be logged to separate log file in the log folder. With the default configuration the logs will be at &lt;code&gt;logs/elasticsearch_index_indexing_slowlog.log&lt;/code&gt; and &lt;code&gt;logs/elasticsearch_index_search_slowlog.log&lt;/code&gt;. The query log will now contain entries like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;[2016-03-23 06:43:47,231][DEBUG][index.search.slowlog.fetch] took[5.8ms], took_millis[5], types[talk], stats[], search_type[QUERY_THEN_FETCH], total_shards[5], source[{&quot;query&quot;:{&quot;match&quot;:{&quot;tags&quot;:&quot;Java&quot;}}}], extra_source[]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;If you are testing this with multiple shards on one instance you might get more log lines than expected: There will be one line for every shard in the query phase and one line for the fetch phase.&lt;/p&gt;   &lt;h4&gt;Runtime Settings&lt;/h4&gt; &lt;p&gt;Besides the setting in &lt;code&gt;elasticsearch.yml&lt;/code&gt; the slow request logs can also be activated using the HTTP API which doesn't require a reboot of the instance and is therefore really well suited for debugging production issues. The following request changes the setting for the query log for an index &lt;code&gt;conference&lt;/code&gt;.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPUT &quot;http://localhost:9200/conference/_settings&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;index.search.slowlog.threshold.query.debug&quot;: &quot;0s&quot;&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;When you are done debugging your issue you can just set a higher threshold again.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>ActiveMQ as a Message Broker for Logstash</title>
   <link href="http://blog.florian-hopf.de/2015/07/activemq-as-message-broker-for-logstash.html"/>
   <updated>2015-07-23T13:53:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2015/07/activemq-as-message-broker-for-logstash</id>
   <content type="html">&lt;p&gt;When scaling Logstash it is common to add a message broker that is used to temporarily buffer incoming messages before they are being processed by one or more Logstash nodes. Data is pushed to the brokers either through a shipper like &lt;a href=&quot;https://github.com/josegonzalez/python-beaver&quot;&gt;Beaver&lt;/a&gt; that reads logfiles and sends each event to the broker. Alternatively the application can send the log events directly using something like a Log4j appender.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-NJR0Pv4qcZ0/VbB6EdXUrtI/AAAAAAAAAbg/SEVwR1ZgAnQ/s1600/overview.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-NJR0Pv4qcZ0/VbB6EdXUrtI/AAAAAAAAAbg/SEVwR1ZgAnQ/s640/overview.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;A common option is to use &lt;a href=&quot;https://ianunruh.com/2014/05/monitor-everything-part-2.html&quot;&gt;Redis as a broker&lt;/a&gt; that stores the data in memory but using other options like Apache Kafka is also possible. Sometimes organizations are not that keen to introduce lots of new technology and want to reuse existing stores. &lt;a href=&quot;http://activemq.apache.org/&quot;&gt;ActiveMQ&lt;/a&gt; is a widely used messaging and integration platform that supports different protocols and looks just perfect for the use as a message broker. Let's see the options to integrate it.&lt;/p&gt; &lt;h4&gt;Setting up ActiveMQ&lt;/h4&gt; &lt;p&gt;ActiveMQ can easily be set up using the scripts that ship with it. On Linux it's just a matter of executing &lt;code&gt;./activemq console&lt;/code&gt;. Using the admin console at http://127.0.0.1:8161/admin/ you can create new queues and even enqueue messages for testing.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-baLz1N4JO5k/VbB6L0181FI/AAAAAAAAAbo/b9hdyEcce4U/s1600/admin-dashboard.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-baLz1N4JO5k/VbB6L0181FI/AAAAAAAAAbo/b9hdyEcce4U/s640/admin-dashboard.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Consuming messages with AMQP&lt;/h4&gt; &lt;p&gt;An obvious way to try to connect ActiveMQ to Logstash is using &lt;a href=&quot;https://www.amqp.org/&quot;&gt;AMQP, the Advanced Message Queuing Protocol&lt;/a&gt;. It's a standard protocol that is supported by different messaging platforms.&lt;/p&gt; &lt;p&gt;There used to be a &lt;a href=&quot;http://logstash.net/docs/1.1.9/tutorials/just-enough-amqp-for-logstash&quot;&gt;Logstash input for AMQP&lt;/a&gt; but unfortunately it has been renamed to &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/current/plugins-inputs-rabbitmq.html&quot;&gt;rabbitmq-input&lt;/a&gt; because RabbitMQ is the main system that is supported.&lt;/p&gt; &lt;p&gt;Let's see what happens if we try to use the input with ActiveMQ.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;input {&lt;br /&gt;    rabbitmq {&lt;br /&gt;        host =&gt; &quot;localhost&quot;&lt;br /&gt;        queue =&gt; &quot;TestQueue&quot;&lt;br /&gt;        port =&gt; 5672&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;output {&lt;br /&gt;    stdout {&lt;br /&gt;        codec =&gt; &quot;rubydebug&quot;&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;   &lt;p&gt;We tell Logstash to listen on localhost on the standard port on a queue named TestQueue. The result should just be dumped to the standard output. Unfortunately Logstash only issues errors because it can't connect.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;Logstash startup completed&lt;br /&gt;RabbitMQ connection error: . Will reconnect in 10 seconds... {:level=&gt;:error}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;In the ActiveMQ logs we can see that our parameters are correct but unfortunately both systems seem to speak different dialects of AMQP.&lt;/p&gt; &lt;pre&gt;&lt;code&gt; WARN | Connection attempt from non AMQP v1.0 client. AMQP,0,0,9,1&lt;br /&gt;org.apache.activemq.transport.amqp.AmqpProtocolException: Connection from client using unsupported AMQP attempted&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;So bad luck with this option.&lt;/p&gt; &lt;h4&gt;Consuming messages with STOMP&lt;/h4&gt; &lt;p&gt;The aptly named &lt;a href=&quot;https://stomp.github.io/&quot;&gt;Simple Text Oriented Messaging Protocol&lt;/a&gt; is another option that is supported by ActiveMQ. Fortunately there is a &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/current/plugins-inputs-stomp.html&quot;&gt;dedicated input for it&lt;/a&gt;. It is not included in Logstash by default but can be installed easily.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;bin/plugin install logstash-input-stomp&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Afterwards we can just use it in our Logstash config.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;input {&lt;br /&gt;    stomp {&lt;br /&gt;        host =&gt; &quot;localhost&quot;&lt;br /&gt;        destination =&gt; &quot;TestQueue&quot;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;output {&lt;br /&gt;    stdout {&lt;br /&gt;        codec =&gt; &quot;rubydebug&quot;&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This time we are better off: Logstash really can connect and dumps our message to the standard output.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;bin/logstash --config stomp.conf &lt;br /&gt;Logstash startup completed&lt;br /&gt;{&lt;br /&gt;       &quot;message&quot; =&gt; &quot;Can I kick it...&quot;,&lt;br /&gt;      &quot;@version&quot; =&gt; &quot;1&quot;,&lt;br /&gt;    &quot;@timestamp&quot; =&gt; &quot;2015-07-22T05:42:35.016Z&quot;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;Consuming messages with JMS&lt;/h4&gt; &lt;p&gt;Though the stomp-input works there is even another option that is not released yet but can already be tested: &lt;a href=&quot;https://github.com/logstash-plugins/logstash-input-jms&quot;&gt;jms-input&lt;/a&gt; supports the Java Messaging System, the standard way of doing messaging on the JVM.&lt;/p&gt; &lt;p&gt;Currently you need to build the plugin yourself (which didn't work on my machine but should be caused by my outdated local jruby installation).&lt;/p&gt; &lt;h4&gt;Getting data in ActiveMQ&lt;/h4&gt; &lt;p&gt;Now that we know of ways to consume data from ActiveMQ it is time to think about how to get data in. When using Java you can use something like a &lt;a href=&quot;http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html&quot;&gt;Log4j-&lt;/a&gt; or &lt;a href=&quot;http://logback.qos.ch/apidocs/ch/qos/logback/classic/net/JMSQueueAppender.html&quot;&gt;Logback&lt;/a&gt;-Appender that push the log events directly to the queue using JMS.&lt;/p&gt; &lt;p&gt;When it comes to shipping data unfortunately none of the more popular solutions seems to be able to push data to ActiveMQ. If you know of any solution that can be used it would be great if you could leave a comment.&lt;/p&gt; &lt;p&gt;All in all I think it can be possible to use ActiveMQ as a broker for Logstash but it might require some more work when it comes to shipping data.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Fixing Elasticsearch Allocation Issues</title>
   <link href="http://blog.florian-hopf.de/2015/02/fixing-elasticsearch-allocation-issues.html"/>
   <updated>2015-02-06T13:59:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2015/02/fixing-elasticsearch-allocation-issues</id>
   <content type="html">&lt;p&gt;Last week I was working with some &lt;a href=&quot;http://logstash.net&quot;&gt;Logstash&lt;/a&gt; data on my laptop. There are around 350 indices that contain the logstash data and an index that holds the metadata for &lt;a href=&quot;http://www.elasticsearch.org/overview/kibana/&quot;&gt;Kibana 4&lt;/a&gt;. When trying to start the single node cluster I have to wait a while, until all indices are available. Some APIs can be used to see the progress of the startup process.&lt;/p&gt; &lt;p&gt;The &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster-health.html&quot;&gt;cluster health API&lt;/a&gt; gives general information about the state of the cluster and indicates if the cluster health is green, yellow or red. After a while the number of unassigned shards didn't change anymore but the cluster still stayed in a red state.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET 'http://localhost:9200/_cluster/health?pretty=true'&lt;br /&gt;{&lt;br /&gt;  &quot;cluster_name&quot; : &quot;elasticsearch&quot;,&lt;br /&gt;  &quot;status&quot; : &quot;red&quot;,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;number_of_nodes&quot; : 1,&lt;br /&gt;  &quot;number_of_data_nodes&quot; : 1,&lt;br /&gt;  &quot;active_primary_shards&quot; : 1850,&lt;br /&gt;  &quot;active_shards&quot; : 1850,&lt;br /&gt;  &quot;relocating_shards&quot; : 0,&lt;br /&gt;  &quot;initializing_shards&quot; : 0,&lt;br /&gt;  &quot;unassigned_shards&quot; : 1852&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;One shard couldn't be recovered: 1850 were ok but it should have been 1851. To see the problem we can use the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cat-indices.html&quot;&gt;cat indices command&lt;/a&gt; that will show us all indices and their health.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl http://localhost:9200/_cat/indices&lt;br /&gt;[...]&lt;br /&gt;yellow open logstash-2014.02.16 5 1 1184 0   1.5mb   1.5mb &lt;br /&gt;red    open .kibana             1 1                        &lt;br /&gt;yellow open logstash-2014.06.03 5 1 1857 0     2mb     2mb &lt;br /&gt;[...]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The &lt;code&gt;.kibana&lt;/code&gt; index didn't turn yellow. It only consists of one primary shard that couldn't be allocated.&lt;/p&gt; &lt;p&gt;Restarting the node and closing and opening the index didn't help. Looking at &lt;a href=&quot;http://blog.florian-hopf.de/2014/06/elasticsearch-kopf.html&quot;&gt;elasticsearch-kopf&lt;/a&gt; I could see that primary and replica shards both were unassingned (You need to tick the checkbox that says hide special to see the index).&lt;/p&gt; &lt;p&gt;Fortunately there is a way to bring the cluster in a yellow state again. We can manually allocate the primary shard on our node.&lt;/p&gt; &lt;p&gt;Elasticsearch provides the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster-reroute.html&quot;&gt;Cluster Reroute API&lt;/a&gt; that can be used to allocate a shard on a node. When trying to allocate the shard of the index &lt;code&gt;.kibana&lt;/code&gt; I first got an exception.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/_cluster/reroute&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;commands&quot; : [ {&lt;br /&gt;          &quot;allocate&quot; : {&lt;br /&gt;              &quot;index&quot; : &quot;.kibana&quot;, &quot;shard&quot; : 0, &quot;node&quot; : &quot;Jebediah Guthrie&quot;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;    ]&lt;br /&gt;}'&lt;br /&gt;&lt;br /&gt;[2015-01-30 13:35:47,848][DEBUG][action.admin.cluster.reroute] [Jebediah Guthrie] failed to perform [cluster_reroute (api)]&lt;br /&gt;org.elasticsearch.ElasticsearchIllegalArgumentException: [allocate] trying to allocate a primary shard [.kibana][0], which is disabled&lt;/code&gt;&lt;/pre&gt; Fortunately the message already tells us the problem: By default you are not allowed to allocate primary shards due to the danger of losing data. If you'd like to allocate a primary shard you need to tell it Elasticsearch explicitly by setting the property allow_primary.  &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/_cluster/reroute&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;commands&quot; : [ {&lt;br /&gt;          &quot;allocate&quot; : {&lt;br /&gt;              &quot;index&quot; : &quot;.kibana&quot;, &quot;shard&quot; : 0, &quot;node&quot; : &quot;Jebediah Guthrie&quot;, &quot;allow_primary&quot;: &quot;true&quot;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;    ]&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;For me this helped and my shard got reallocated and the cluster health turned yellow.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-0XqYtZ6DBBo/VNRYAEbbJ0I/AAAAAAAAAao/h7iVwpAQv9g/s1600/kibana-reallocated.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-0XqYtZ6DBBo/VNRYAEbbJ0I/AAAAAAAAAao/h7iVwpAQv9g/s320/kibana-reallocated.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;I am not sure what caused the problems but it is very likely related to the way I am working locally. I am regularly sending my laptop to sleep which is something you never do on a server. Nevertheless I have seen this problem a few times locally which justifies writing down the necessary steps to fix it.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Logging to Redis using Spring Boot and Logback</title>
   <link href="http://blog.florian-hopf.de/2015/01/logging-to-redis-using-spring-boot-and.html"/>
   <updated>2015-01-23T15:15:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2015/01/logging-to-redis-using-spring-boot-and</id>
   <content type="html">&lt;p&gt;When doing centralized logging, e.g. using &lt;a href=&quot;http://www.elasticsearch.org/overview/&quot;&gt;Elasticsearch, Logstash and Kibana&lt;/a&gt; or &lt;a href=&quot;https://www.graylog2.org/&quot;&gt;Graylog2&lt;/a&gt; you have several options available for your Java application. You can either write your standard application logs and parse those using &lt;a href=&quot;http://logstash.net&quot;&gt;Logstash&lt;/a&gt;, either consumed directly or shipped to another machine using something like &lt;a href=&quot;https://github.com/elasticsearch/logstash-forwarder&quot;&gt;logstash-forwarder&lt;/a&gt;. Alternatively you can write in a more appropriate format like JSON directly so the processing step doesn't need that much work for parsing your messages. As a third option is to write to a different data store directly which acts as a buffer for your log messages. In this post we are looking at how we can configure &lt;a href=&quot;http://logback.qos.ch/&quot;&gt;Logback&lt;/a&gt; in a &lt;a href=&quot;http://projects.spring.io/spring-boot/&quot;&gt;Spring Boot&lt;/a&gt; application to write the log messages to &lt;a href=&quot;http://redis. io/&quot;&gt;Redis&lt;/a&gt; directly.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-yism2ahB0ps/VMHxlUksf9I/AAAAAAAAAaU/e5Mg_eur-U0/s1600/overview-redis-logstash.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-yism2ahB0ps/VMHxlUksf9I/AAAAAAAAAaU/e5Mg_eur-U0/s400/overview-redis-logstash.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Redis&lt;/h4&gt; &lt;p&gt;We are using Redis as a log buffer for our messages. Not everyone is happy with Redis but it is a common choice. Redis stores its content in memory which makes it well suited for fast access but can also sync it to disc when necessary. A special feature of Redis is that the values can be &lt;a href=&quot;http://redis.io/topics/data-types&quot;&gt;different data types&lt;/a&gt; like strings, lists or sets. Our application uses a single key and value pair where the key is the name of the application and the value is a list that contains all our log messages. This way we can handle several logging applications in one Redis instance.&lt;/p&gt; &lt;p&gt;When testing your setup you might also want to look into the data that is stored in Redis. You can access it using the &lt;a href=&quot;http://redis.io/topics/quickstart&quot;&gt;redis-cli client&lt;/a&gt;. I collected some useful &lt;a href=&quot;http://redis.io/commands&quot;&gt;commands&lt;/a&gt; for validating your log messages are actually written to Redis.&lt;/p&gt; &lt;table class=&quot;table&quot;&gt;&lt;tr&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;KEYS *&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Show all keys in this Redis instance&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;LLEN key&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Show the number of messages in the list for &lt;code&gt;key&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;LRANGE key 0 100&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Show the first 100 messages in the list for &lt;code&gt;key&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;h4&gt;The Logback Config&lt;/h4&gt; &lt;p&gt;When working with Logback most of the time an XML file is used for all the configuration. Appenders are the things that send the log output somewhere. Loggers are used to set log levels and attach appenders to certain pieces of the application.&lt;/p&gt;   &lt;p&gt;For Spring Boot Logback is available for any application that uses the &lt;a href=&quot;http://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html&quot;&gt;spring-boot-starter-logging&lt;/a&gt; which is also a dependency of the common spring-boot-starter-web. The configuration can be added to a file called &lt;code&gt;logback.xml&lt;/code&gt; that resides in &lt;code&gt;src/main/resources&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;Spring boot comes with a file and a console appender that are already configured correctly. We can include the base configuration in our file to keep all the predefined configurations.&lt;/p&gt; &lt;p&gt;For logging to Redis we need to add another appender. A good choice is the &lt;a href=&quot;https://github.com/kmtong/logback-redis-appender&quot;&gt;logback-redis-appender&lt;/a&gt; that is rather lightweight and uses the Java client Jedis. The log messages are written to Redis in JSON directly so it's a perfect match for something like logstash. We can make Spring Boot log to a local instance of Redis by using the following configuration.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;    &amp;lt;include resource=&amp;quot;org/springframework/boot/logging/logback/base.xml&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;appender name=&amp;quot;LOGSTASH&amp;quot; class=&amp;quot;com.cwbase.logback.RedisAppender&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;host&amp;gt;localhost&amp;lt;/host&amp;gt;&lt;br /&gt;        &amp;lt;port&amp;gt;6379&amp;lt;/port&amp;gt;&lt;br /&gt;        &amp;lt;key&amp;gt;my-spring-boot-app&amp;lt;/key&amp;gt;&lt;br /&gt;    &amp;lt;/appender&amp;gt;&lt;br /&gt;    &amp;lt;root level=&amp;quot;INFO&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;appender-ref ref=&amp;quot;LOGSTASH&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;appender-ref ref=&amp;quot;CONSOLE&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;appender-ref ref=&amp;quot;FILE&amp;quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/root&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We configure an appender named &lt;code&gt;LOGSTASH&lt;/code&gt; that is an instance of &lt;code&gt;RedisAppender&lt;/code&gt;. Host and port are set for a local Redis instance, key identifies the Redis key that is used for our logs. There are more options available like the interval to push log messages to Redis. Explore the &lt;a href=&quot;https://github.com/kmtong/logback-redis-appender&quot;&gt;readme of the project&lt;/a&gt; for more information.&lt;/p&gt; &lt;h4&gt;Spring Boot Dependencies&lt;/h4&gt; &lt;p&gt;To make the logging work we of course have to add a dependency to the &lt;code&gt;logback-redis-appender&lt;/code&gt; to our pom. Depending on your Spring Boot version you might see some errors in your log file that methods are missing.&lt;/p&gt; &lt;p&gt;This is because Spring Boot &lt;a href=&quot;https://github.com/spring-projects/spring-boot/blob/master/spring-boot-dependencies/pom.xml&quot;&gt;manages the dependencies it uses internally&lt;/a&gt; and the versions for jedis and commons-pool2 do not match the ones that we need. If this happens we can configure the versions to use in the properties section of our pom.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;properties&amp;gt;&lt;br /&gt;    &amp;lt;commons-pool2.version&amp;gt;2.0&amp;lt;/commons-pool2.version&amp;gt;&lt;br /&gt;    &amp;lt;jedis.version&amp;gt;2.5.2&amp;lt;/jedis.version&amp;gt;&lt;br /&gt;&amp;lt;/properties&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now the application will start and you can see that it sends the log messages to Redis as well.&lt;/p&gt; &lt;h4&gt;Enhancing the Configuration&lt;/h4&gt; &lt;p&gt;Having the host and port configured in the &lt;code&gt;logback.xml&lt;/code&gt; is not the best thing to do. When deploying to another environment with different settings you have to change the file or deploy a custom one.&lt;/p&gt;   &lt;p&gt;The Spring Boot integration of Logback allows to set some of the configuration options like the file to log to and the log levels using the main configuration file &lt;code&gt;application.properties&lt;/code&gt;. Unfortunately this is a special treatment for some values and you can't add custom values as far as I could see.&lt;/p&gt; &lt;p&gt;But fortunately Logback supports the use of environment variables so we don't have to rely on configuration files. Having set the environment variables &lt;code&gt;REDIS_HOST&lt;/code&gt; and &lt;code&gt;REDIS_PORT&lt;/code&gt; you can use the following configuration for your appender.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;    &amp;lt;appender name=&amp;quot;LOGSTASH&amp;quot; class=&amp;quot;com.cwbase.logback.RedisAppender&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;host&amp;gt;${REDIS_HOST}&amp;lt;/host&amp;gt;&lt;br /&gt;        &amp;lt;port&amp;gt;${REDIS_PORT}&amp;lt;/port&amp;gt;&lt;br /&gt;        &amp;lt;key&amp;gt;my-spring-boot-app&amp;lt;/key&amp;gt;&lt;br /&gt;    &amp;lt;/appender&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We can even go one step further. To only activate the appender when the property is set you can add conditional processing to your configuration.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;    &amp;lt;if condition=&amp;#39;isDefined(&amp;quot;REDIS_HOST&amp;quot;) &amp;amp;amp;&amp;amp;amp; isDefined(&amp;quot;REDIS_PORT&amp;quot;)&amp;#39;&amp;gt;&lt;br /&gt;        &amp;lt;then&amp;gt;&lt;br /&gt;            &amp;lt;appender name=&amp;quot;LOGSTASH&amp;quot; class=&amp;quot;com.cwbase.logback.RedisAppender&amp;quot;&amp;gt;&lt;br /&gt;                &amp;lt;host&amp;gt;${REDIS_HOST}&amp;lt;/host&amp;gt;&lt;br /&gt;                &amp;lt;port&amp;gt;${REDIS_PORT}&amp;lt;/port&amp;gt;&lt;br /&gt;                &amp;lt;key&amp;gt;my-spring-boot-app&amp;lt;/key&amp;gt;&lt;br /&gt;            &amp;lt;/appender&amp;gt;&lt;br /&gt;        &amp;lt;/then&amp;gt;&lt;br /&gt;    &amp;lt;/if&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can use a Java expression for deciding if the block should be evaluated. When the appender is not available Logback will just log an error and uses any other appenders that are configured. For this to work you need to &lt;a href=&quot;http://logback.qos.ch/setup.html#janino&quot;&gt;add the Janino library to your pom&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Now the appender is activated based on the environment variables. If you like you can skip the setup for local development and only set the variables on production systems.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Getting started with Spring Boot or logging to Redis alone is very easy but some of the details are some work to get right. But it's worth the effort: Once you get used to centralized logging you don't want to have your systems running without it anymore.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Use Cases for Elasticsearch: Analytics</title>
   <link href="http://blog.florian-hopf.de/2014/12/use-cases-for-elasticsearch-analytics.html"/>
   <updated>2014-12-12T17:35:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/12/use-cases-for-elasticsearch-analytics</id>
   <content type="html">&lt;p&gt;In the &lt;a href=&quot;http://blog.florian-hopf.de/2014/09/use-cases-for-elasticsearch-index-and.html&quot;&gt;last post&lt;/a&gt; in &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/slides-for-use-cases-for-elasticsearch.html&quot;&gt;this series&lt;/a&gt; we have seen how we can use Logstash, Elasticsearch and Kibana for doing logfile analytics. This week we will look at the general capabilities for doing analytics on any data using Elasticsearch and Kibana.&lt;/p&gt; &lt;h4&gt;Use Case&lt;/h4&gt; &lt;p&gt;We have already seen that Elasticsearch can be used to &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-document.html&quot;&gt;store large amounts of data&lt;/a&gt;. Instead of putting data into a data warehouse Elasticsearch can be used to do analytics and reporting. Another use case is &lt;a href=&quot;http://blog.florian-hopf.de/2013/09/simple-event-analytics-with.html&quot;&gt;social media data&lt;/a&gt;: Companies can look at what happens with their brand if they have the possibility to easily search it. Data can be ingested from multiple sources, e.g. Twitter and Facebook and combined in one system. &lt;a href=&quot;http://blog.florian-hopf.de/2013/09/kibana-and-elasticsearch-see-what.html&quot;&gt;Visualizing data in tools like Kibana&lt;/a&gt; can help with exploring large data sets. Finally mechanisms like Elasticsearchs &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html&quot;&gt;Aggregations&lt;/a&gt; can help with finding new ways to look at the data.&lt;/p&gt; &lt;h4&gt;Aggregations&lt;/h4&gt; &lt;p&gt;Aggregations provide what the now deprecated facets have been providing but also a lot more. They can combine and count values from different documents and therefore show you what is contained in your data. For example if you have tweets indexed in Elasticsearch you can use the terms aggregation to find the most common hashtags. &lt;i&gt;For details on &lt;a href=&quot;http://blog.florian-hopf.de/2013/09/simple-event-analytics-with.html&quot;&gt;indexing tweets in Elasticsearch see this post on the Twitter River&lt;/a&gt; and &lt;a href=&quot;http://blog.florian-hopf.de/2014/06/an-alternative-to-twitter-river-index.html&quot;&gt;this post on the Twitter input for Logstash&lt;/a&gt;.&lt;/i&gt;&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/devoxx/tweet/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;aggs&quot; : {&lt;br /&gt;        &quot;hashtags&quot; : {&lt;br /&gt;            &quot;terms&quot; : { &lt;br /&gt;                &quot;field&quot; : &quot;hashtag.text&quot; &lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Aggregations are requested using the aggs keyword, &lt;code&gt;hashtags&lt;/code&gt; is a name I have chosen to identify the result and the terms aggregation counts the different terms for the given field (Disclaimer: For a sharded setup the terms aggregation might not be totally exact). This request might result in something like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&quot;aggregations&quot;: {&lt;br /&gt;      &quot;hashtags&quot;: {&lt;br /&gt;         &quot;buckets&quot;: [&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;dartlang&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 229&lt;br /&gt;            },&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;java&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 216&lt;br /&gt;            },&lt;br /&gt;[...]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The result is available for the name we have chosen. Aggregations put the counts into buckets that contain of a value and a count. This is very similar to how faceting works, only the names are different. For this example we can see that there are 229 documents for the hashtag &lt;code&gt;dartlang&lt;/code&gt; and 216 containing the hashtag &lt;code&gt;java&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;This could also be done with facets alone but there is more: Aggregations can even be combined. You can now nest another aggregation in the first one that for every bucket will give you more buckets for another criteria.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/devoxx/tweet/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;aggs&quot; : {&lt;br /&gt;        &quot;hashtags&quot; : {&lt;br /&gt;            &quot;terms&quot; : { &lt;br /&gt;                &quot;field&quot; : &quot;hashtag.text&quot; &lt;br /&gt;            },&lt;br /&gt;            &quot;aggs&quot; : {&lt;br /&gt;                &quot;hashtagusers&quot; : {&lt;br /&gt;                    &quot;terms&quot; : {&lt;br /&gt;                        &quot;field&quot; : &quot;user.screen_name&quot;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We still request the terms aggregation for the hashtag. But now we have another aggregation embedded, a terms aggregation that processes the user name. This will then result in something like this.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;               &quot;key&quot;: &quot;scala&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 130,&lt;br /&gt;               &quot;hashtagusers&quot;: {&lt;br /&gt;                  &quot;buckets&quot;: [&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;jaceklaskowski&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 74&lt;br /&gt;                     },&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;ManningBooks&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 3&lt;br /&gt;                     },&lt;br /&gt;    [...]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We can now see the users that have used a certain hashtext. In this case one user used one hashtag a lot. This is information that is not available that easily with queries and facets alone.&lt;/p&gt; &lt;p&gt;Besides the terms aggreagtion we have seen here there are also &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html&quot;&gt;lots of other interesting aggregations available&lt;/a&gt; and more are added with every release. You can choose between bucket aggregations (like the terms aggregation) and metrics aggregations, that calculate values from the buckets, e.g. averages oder other statistical values.&lt;/p&gt;   &lt;h4&gt;Visualizing the Data&lt;/h4&gt; &lt;p&gt;Besides the JSON output we have seen above, the data can also be used for visualizations. This is something that can then be prepared even for a non technical audience. Kibana is one of the options that is often used for logfile data but can be used for data of all kind, e.g. the Twitter data we have already seen above.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-yls_NyBbR-8/VIq6nQPz62I/AAAAAAAAAZ4/G6abNb-J9oc/s1600/overall2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-yls_NyBbR-8/VIq6nQPz62I/AAAAAAAAAZ4/G6abNb-J9oc/s640/overall2.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;There are two bar charts that display the term frequencies for the mentions and the hashtags. We can already see easily which values are dominant. Also, the date histogram to the right shows at what time most tweets are sent. All in all these visualizations can provide a lot of value when it comes to trends that are only seen when combining the data.&lt;/p&gt; &lt;p&gt;&lt;i&gt;The image shows Kibana 3, which still relies on the facet feature. &lt;a href=&quot;http://www.elasticsearch.org/blog/kibana-4-beta-1-released/&quot;&gt;Kibana 4&lt;/a&gt; will instead provide access to the aggregations.&lt;/i&gt;&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;This post ends the series on use cases for Elasticsearch. I hope you enjoyed reading it and maybe you learned something new along the way. I can't spend that much time blogging anymore but new posts will be coming. Keep an eye on this blog.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Use Cases for Elasticsearch: Index and Search Log Files</title>
   <link href="http://blog.florian-hopf.de/2014/09/use-cases-for-elasticsearch-index-and.html"/>
   <updated>2014-09-19T14:19:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/09/use-cases-for-elasticsearch-index-and</id>
   <content type="html">&lt;p&gt;In the &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/slides-for-use-cases-for-elasticsearch.html&quot;&gt;last posts&lt;/a&gt; we have seen some of the properties of using Elasticsearch as a &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-document.html&quot;&gt;document store&lt;/a&gt;, for &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-full-text.html&quot;&gt;searching text content&lt;/a&gt; and &lt;a href=&quot;http://blog.florian-hopf.de/2014/08/use-cases-for-elasticsearch-geospatial.html&quot;&gt;geospatial search&lt;/a&gt;. In this post we will look at how it can be used to index and store log files, a very useful application that can help developers and operations in maintaining applications.&lt;/p&gt; &lt;h4&gt;Logging&lt;/h4&gt; &lt;p&gt;When maintaining larger applications that are either distributed across several nodes or consist of several smaller applications searching for events in log files can become tedious. You might already have been in the situation that you have to find an error and need to log in to several machines and look at several log files. Using Linux tools like grep can be fun sometimes but there are more convenient ways. Elasticsearch and the projects Logstash and Kibana, commonly known as the &lt;a href=&quot;http://www.elasticsearch.org/overview/&quot;&gt;ELK stack&lt;/a&gt;, can help you with this.&lt;/p&gt; &lt;p&gt;With the ELK stack you can centralize your logs by indexing them in Elasticsearch. This way you can use Kibana to look at all the data without having to log in on the machine. This can also make Operations happy as they don't have to grant access to every developer who needs to have access to the logs. As there is one central place for all the logs you can even see different applications in context. For example you can see the logs of your Apache webserver combined with the log files of your application server, e.g. Tomcat. As search is core to what Elasticsearch is doing you should be able to find what you are looking for even more quickly.&lt;/p&gt; &lt;p&gt;Finally Kibana can also help you with becoming more proactive. As all the information is available in real time you also have a visual representation of what is happening in your system in real time. This can help you in finding problems more quickly, e.g. you can see that some resource starts throwing Exceptions without having your customers report it to you.&lt;/p&gt; &lt;h4&gt;The ELK Stack&lt;/h4&gt; &lt;p&gt;For logfile analytics you can use all three applications of the ELK stack: Elasticsearch, Logstash and Kibana. Logstash is used to read and enrich the information from log files. Elasticsearch is used to store all the data and Kibana is the frontend that provides dashboards to look at the data.&lt;/p&gt; &lt;p&gt;The logs are fed into Elasticsearch using Logstash that combines the different sources. Kibana is used to look at the data in Elasticsearch. This setup has the advantage that different parts of the log file processing system can be scaled differently. If you need more storage for the data you can add more nodes to the Elasticsearch cluster. If you need more processing power for the log files you can add more nodes for Logstash.&lt;/p&gt; &lt;h4&gt;Logstash&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://logstash.net&quot;&gt;Logstash&lt;/a&gt; is a JRuby application that can read input from several sources, modify it and push it to a multitude of outputs. For running Logstash you need to pass it a configuration file that determines where the data is and what should be done with it. The configuration normally consists of an &lt;code&gt;input&lt;/code&gt; and an &lt;code&gt;output&lt;/code&gt; section and an optional &lt;code&gt;filter&lt;/code&gt; section. This example takes the Apache access logs, does some predefined processing and stores them in Elasticsearch:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;input {&lt;br /&gt;  file {&lt;br /&gt;    path =&gt; &quot;/var/log/apache2/access.log&quot;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;filter {&lt;br /&gt;  grok {&lt;br /&gt;    match =&gt; { message =&gt; &quot;%{COMBINEDAPACHELOG}&quot; }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;output {&lt;br /&gt;  elasticsearch_http {&lt;br /&gt;    host =&gt; &quot;localhost&quot;&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The file input reads the log files from the path that is supplied. In the filter section we have defined the &lt;a href=&quot;http://logstash.net/docs/1.4.2/filters/grok&quot;&gt;grok filter&lt;/a&gt; that parses unstructured data and structures it. It comes with lots of predefined patterns for different systems. In this case we are using the complete Apache log pattern but there are also more basic building block like parsing email and ip addresses and dates (which can be lots of fun with all the different formats).&lt;/p&gt; &lt;p&gt;In the output section we are telling Logstash to push the data to Elasticsearch using http. We are using a server on localhost, for most real world setups this would be a cluster on separate machines.&lt;/p&gt; &lt;h4&gt;Kibana&lt;/h4&gt; &lt;p&gt;Now that we have the data in Elasticsearch we want to look at it. Kibana is a JavaScript application that can be used to build dashboards. It accesses Elasticsearch from the browser so whoever uses Kibana needs to have access to Elasticsearch.&lt;/p&gt; &lt;p&gt;When using it with Logstash you can open a predefined dashboard that will pull some information from your index. You can then display charts, maps and tables for the data you have indexed. This screenshot displays a histogram and a table of log events but there are more widgets available like maps and pie and bar charts.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-JbJbjv9Rn2g/VBr1a7KKTuI/AAAAAAAAAYw/ODXYDAQYgaw/s1600/kibana-logging.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-JbJbjv9Rn2g/VBr1a7KKTuI/AAAAAAAAAYw/ODXYDAQYgaw/s400/kibana-logging.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;As you can see you can extract a lot of data visually that would otherwise be buried in several log files.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;The ELK stack can be a great tool to read, modify and store log events. Dashboards help with visualizing what is happening. There are lots of inputs in Logstash and the grok filter supplies lots of different formats. Using those tools you can consolidate and centralize all your log files.&lt;/p&gt; &lt;p&gt;Lots of people are using the stack for analyzing their log file data. One of the articles that is available is by &lt;a href=&quot;http://www.elasticsearch.org/blog/using-elasticsearch-and-logstash-to-serve-billions-of-searchable-events-for-customers/&quot;&gt;Mailgun, who are using it to store billions of events.&lt;/a&gt; And if that's not enough read &lt;a href=&quot;https://medium.com/@ghoranyi/needle-in-a-haystack-873c97a99983&quot;&gt;this post on how CERN uses the ELK stack to help running the Large Hadron Collider&lt;/a&gt;&lt;/p&gt; &lt;p&gt;In the next post we will look at the final use case for Elasticsearch: Analytics.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Use Cases for Elasticsearch: Geospatial Search</title>
   <link href="http://blog.florian-hopf.de/2014/08/use-cases-for-elasticsearch-geospatial.html"/>
   <updated>2014-08-29T13:11:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/08/use-cases-for-elasticsearch-geospatial</id>
   <content type="html">&lt;p&gt;In the &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/slides-for-use-cases-for-elasticsearch.html&quot;&gt;previous posts&lt;/a&gt; we have seen that Elasticsearch can be used to &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-document.html&quot;&gt;store documents in JSON format and distribute the data across multiple nodes as shards and replicas&lt;/a&gt;. Lucene, the underlying library, provides the implementation of the inverted index that can be used to &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-full-text.html&quot;&gt;search the documents&lt;/a&gt;. Analyzing is a crucial step for building a good search application.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-ELKSqM6JCQA/VAAGg1KvloI/AAAAAAAAAYc/PwtwkJn8jSk/s640/file2331343883319.jpg&quot; /&gt;&lt;/div&gt; &lt;p&gt;In this post we will look at a different feature that can be used for applications you would not immediately associate Elasticsearch with. We will look at the geo features that can be used to build applications that can filter and sort documents based on the location of the user.&lt;/p&gt; &lt;h4&gt;Locations in Applications&lt;/h4&gt; &lt;p&gt;Location based features can be useful for a wide range of applications. For merchants the web site can present the closest point of service for the current user. Or there is a search facility for finding points of services according to a location, often integrated with something like Google Maps. For classifieds it can make sense to sort them by distance from the user searching, the same is true for any search for locations like restaurants and the like. Sometimes it also makes sense to only show results that are in a certain area around me, in this case we need to filter by distance. Probably the user is looking for a new appartment and is not interested in results that are too far away from his workplace. Finally locations can also be of interest when doing analytics. Social media data can tell you where something interesting is happening just by looking at the amount of status messages sent from a certain area.&lt;/p&gt; &lt;p&gt;Most of the time locations are stored as a pair of latitude and longitude, which denotes a point. The combination of 48.779506, 9.170045 for example points to Liederhalle Stuttgart which happens to be the location for Java Forum Stuttgart. &lt;a href=&quot;http://en.wikipedia.org/wiki/Geohash&quot;&gt;Geohashes&lt;/a&gt; are an alternative means to encode latitude and longitude. They can be stored in arbitrary precision so those can also refer to a larger area instead of a point.&lt;/p&gt; &lt;p&gt;When calculating a Geohash the map is divided into several buckets or cells. Each bucket is identified by a base 32 encoded value. The complete geohash then consists of a sequence of characters. Each following character marks the bucket in the previous bucket so you are zooming in to the location. The longer the geohash string the more precise the location is. For example &lt;code&gt;u0wt88j3jwcp&lt;/code&gt; is the geohash for Liederhalle Stuttgart. The prefix &lt;code&gt;u0wt&lt;/code&gt; on the other hand is the area of Stuttgart and some of the surrounding cities.&lt;/p&gt;   &lt;p&gt;The hierarchical nature of geohashes and the possiblity to express them as strings makes them a good choice for storing them in the inverted index. You can create geohashes using the &lt;a href=&quot;http://geohash.org/&quot;&gt;original geohash service&lt;/a&gt; or &lt;a href=&quot;http://geohash.gofreerange.com/&quot;&gt;more visually appealing using the nice GeohashExplorer&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Locations in Elasticsearch&lt;/h4&gt; &lt;p&gt;Elasticsearch accepts &lt;code&gt;lat&lt;/code&gt; and &lt;code&gt;lon&lt;/code&gt; for specifying latitude and longitude. These are two documents for a conference in Stuttgart and one in Nuremberg.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt;    &quot;title&quot; : &quot;Anwendungsfälle für Elasticsearch&quot;,&lt;br /&gt;    &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;    &quot;date&quot; : &quot;2014-07-17T15:35:00.000Z&quot;,&lt;br /&gt;    &quot;tags&quot; : [&quot;Java&quot;, &quot;Lucene&quot;],&lt;br /&gt;    &quot;conference&quot; : {&lt;br /&gt;        &quot;name&quot; : &quot;Java Forum Stuttgart&quot;,&lt;br /&gt;        &quot;city&quot; : &quot;Stuttgart&quot;,&lt;br /&gt;            &quot;coordinates&quot;: {&lt;br /&gt;                &quot;lon&quot;: &quot;9.170045&quot;,&lt;br /&gt;                &quot;lat&quot;: &quot;48.779506&quot;&lt;br /&gt;            }&lt;br /&gt;    } &lt;br /&gt;}&lt;br /&gt;{&lt;br /&gt;    &quot;title&quot; : &quot;Anwendungsfälle für Elasticsearch&quot;,&lt;br /&gt;    &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;    &quot;date&quot; : &quot;2014-07-15T16:30:00.000Z&quot;,&lt;br /&gt;    &quot;tags&quot; : [&quot;Java&quot;, &quot;Lucene&quot;],&lt;br /&gt;    &quot;conference&quot; : {&lt;br /&gt;        &quot;name&quot; : &quot;Developer Week&quot;,&lt;br /&gt;        &quot;city&quot; : &quot;Nürnberg&quot;,&lt;br /&gt;            &quot;coordinates&quot;: {&lt;br /&gt;                &quot;lon&quot;: &quot;11.115358&quot;,&lt;br /&gt;                &quot;lat&quot;: &quot;49.417175&quot;&lt;br /&gt;            }&lt;br /&gt;    } &lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Alternatively you can use the &lt;a href=&quot;http://geojson.org/&quot;&gt;GeoJSON&lt;/a&gt; format, accepting an array of longitude and latitude. If you are like me be prepared to hunt down why queries aren't working just to notice that you messed up the order in the array.&lt;/p&gt; &lt;p&gt;The field needs to be mapped with a &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-geo-point-type.html&quot;&gt;geo_point&lt;/a&gt; field type.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt;    &quot;properties&quot;: {&lt;br /&gt;          […],&lt;br /&gt;       &quot;conference&quot;: {&lt;br /&gt;            &quot;type&quot;: &quot;object&quot;,&lt;br /&gt;            &quot;properties&quot;: {&lt;br /&gt;                &quot;coordinates&quot;: {&lt;br /&gt;                    &quot;type&quot;: &quot;geo_point&quot;,&lt;br /&gt;                    &quot;geohash&quot;: &quot;true&quot;,&lt;br /&gt;                    &quot;geohash_prefix&quot;: &quot;true&quot;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;       }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;By passing the optional attribute &lt;code&gt;geohash&lt;/code&gt; Elasticsearch will automatically store the geohash for you as well. Depending on your usecase you can also store all the parent cells of the geohash using the parameter &lt;code&gt;geohash_prefix&lt;/code&gt;. As the values are just strings this is a normal ngram index operation which stores the different substrings for a term, e.g. u, u0, u0w and u0wt for u0wt.&lt;/p&gt; &lt;p&gt;With our documents in place we can now use the geo information for sorting, filtering and aggregating results.&lt;/p&gt; &lt;h4&gt;Sorting by Distance&lt;/h4&gt; &lt;p&gt;First, let's sort all our documents &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_geo_distance_sorting&quot;&gt;by distance from a point&lt;/a&gt;. This would allow us to build an application that displays the closest location for the current user.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/conferences/_search &quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;sort&quot; : [&lt;br /&gt;        {&lt;br /&gt;            &quot;_geo_distance&quot; : {&lt;br /&gt;                &quot;conference.coordinates&quot; : {&lt;br /&gt;                    &quot;lon&quot;: 8.403697,&lt;br /&gt;                    &quot;lat&quot;: 49.006616&lt;br /&gt;                },&lt;br /&gt;                &quot;order&quot; : &quot;asc&quot;,&lt;br /&gt;                &quot;unit&quot; : &quot;km&quot;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    ]&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We are requesting to sort by &lt;code&gt;_geo_distance&lt;/code&gt; and are passing in another location, this time Karlsruhe, where I live. Results should be sorted ascending so the closer results come first. As Stuttgart is not far from Karlsruhe it will be first in the list of results.&lt;/p&gt; &lt;p&gt;The score for the document will be empty. Instead there is a field sort that contains the distance of the locations from the one provided. This can be really handy when displaying the results to the user.&lt;/p&gt; &lt;h4&gt;Filtering by Distance&lt;/h4&gt; &lt;p&gt;For some usecase we would like to &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-filter.html&quot;&gt;filter our results by distance&lt;/a&gt;. Some online real estate agencies for example provide the option to only display results that are in a certain distance from a point. We can do the same by passing in a &lt;code&gt;geo_distance&lt;/code&gt; filter.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/conferences/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;   &quot;filter&quot;: {&lt;br /&gt;      &quot;geo_distance&quot;: {&lt;br /&gt;         &quot;conference.coordinates&quot;: {&lt;br /&gt;            &quot;lon&quot;: 8.403697,&lt;br /&gt;            &quot;lat&quot;: 49.006616&lt;br /&gt;         },&lt;br /&gt;         &quot;distance&quot;: &quot;200km&quot;,&lt;br /&gt;         &quot;distance_type&quot;: &quot;arc&quot;&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We are again passing the location of Karlsruhe. We request that only documents in a distance of 200km should be returned and that the &lt;code&gt;arc&lt;/code&gt; &lt;code&gt;distance_type&lt;/code&gt; should be used for calculating the distance. This will take into account that we are living on a globe.&lt;/p&gt; &lt;p&gt;The resulting list will only contain one document, Stuttgart, as Nuremberg is just over 200km away. If we use the distance 210km both of the documents will be returned.&lt;/p&gt; &lt;h4&gt;Geo Aggregations&lt;/h4&gt; &lt;p&gt;Elasticsearch provides several useful geo &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html&quot;&gt;aggregations&lt;/a&gt; that allow you to retrieve more information on the locations of your documents, e.g. for faceting. On the other hand as we do have the geohash as well as the prefix enabled we can retrieve all of the cells our results are in using a simple &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html&quot;&gt;terms aggregation&lt;/a&gt;. This way you can let the user drill down on the results by filtering on the cell.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/conferences/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;aggregations&quot; : {&lt;br /&gt;        &quot;conference-hashes&quot; : {&lt;br /&gt;            &quot;terms&quot; : {&lt;br /&gt;                &quot;field&quot; : &quot;conference.coordinates.geohash&quot;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Depending on the precision we have chosen while indexing this will return a long list of prefixes for hashes but the most important part is at the beginning.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;[...]&lt;br /&gt;   &quot;aggregations&quot;: {&lt;br /&gt;      &quot;conference-hashes&quot;: {&lt;br /&gt;         &quot;buckets&quot;: [&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;u&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 2&lt;br /&gt;            },&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;u0&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 2&lt;br /&gt;            },&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;u0w&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 1&lt;br /&gt;            },&lt;br /&gt;            [...]&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Stuttgart and Nuremberg both share the parent cells u and u0.&lt;/p&gt; &lt;p&gt;Alternatively to the terms aggregation you can also use specialized geo aggregations, e.g. the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geodistance-aggregation.html&quot;&gt;geo distance aggregation&lt;/a&gt; for forming buckets of distances.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Besides the features we have seen here Elasticsearch offers a wide range of geo features. You can &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-geo-shape-type.html&quot;&gt;index shapes&lt;/a&gt; and &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-geo-shape-filter.html&quot;&gt;query by arbitrary polygons&lt;/a&gt;, either by passing them in or by passing a reference of an indexed polygon. When geohash prefixes are turned on you can also filter by geohash cell.&lt;/p&gt;   &lt;p&gt;With the new HTML 5 location features location aware search and content delivery will become more important. Elasticsearch is a good fit for building this kind of applications.&lt;/p&gt; &lt;p&gt;Two users in the geo space are &lt;a href=&quot;http://engineering.foursquare.com/2012/08/09/foursquare-now-uses-elastic-search-and-on-a-related-note-slashem-also-works-with-elastic-search/&quot;&gt;Foursquare, a very early user of Elasticsearch&lt;/a&gt;, and &lt;a href=&quot;http://www.elasticsearch.org/case-study/gild/&quot;&gt;Gild, a recruitment agency that does some magic with locations&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Resources for Freelancers</title>
   <link href="http://blog.florian-hopf.de/2014/08/resources-for-freelancers.html"/>
   <updated>2014-08-13T14:03:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/08/resources-for-freelancers</id>
   <content type="html">&lt;p&gt;More than half a year ago I wrote a post on &lt;a href=&quot;http://blog.florian-hopf.de/2014/01/20-months-of-freelancing.html&quot;&gt;my first years as a freelancer&lt;/a&gt;. While writing the post I noticed that there are quite some resources I would like to recommend which I deferred to another post that never was written. Last weekend at &lt;a href=&quot;http://www.socrates-conference.de/&quot;&gt;SoCraTes&lt;/a&gt; we had a very productive session on freelancing. We talked about different aspects from getting started, kinds of and reasons for freelancing to handling your sales pipeline.&lt;/p&gt;   &lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;de&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/hashtag/freelancing?src=hash&quot;&gt;#freelancing&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/session?src=hash&quot;&gt;#session&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/mind?src=hash&quot;&gt;#mind&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/map?src=hash&quot;&gt;#map&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/summary?src=hash&quot;&gt;#summary&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/socrates14?src=hash&quot;&gt;#socrates14&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/softwerkskammer?src=hash&quot;&gt;#softwerkskammer&lt;/a&gt; &lt;a href=&quot;http://t.co/JAkFMfaOHZ&quot;&gt;pic.twitter.com/JAkFMfaOHZ&lt;/a&gt;&lt;/p&gt;&amp;mdash; Benjamin (@dataduke) &lt;a href=&quot;https://twitter.com/dataduke/statuses/497688175595507712&quot;&gt;8. August 2014&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt; &lt;p&gt;&lt;a href=&quot;http://davidtanzer.net/&quot;&gt;David&lt;/a&gt; and me recommended some resources on getting started so this is the perfect excuse to write the post I planned to write initially.&lt;/p&gt; &lt;p&gt;I will keep it minimal and only present a short description of each point.&lt;/p&gt; &lt;dl&gt;&lt;dt&gt;&lt;a href=&quot;https://www.softwerkskammer.org/groups/freelance-topics&quot;&gt;Softwerkskammer Freelancer Group&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;Our discussion at SoCraTes lead to founding a new group in the Softwerkskammer community. We plan to exchange knowledge and probably even work opportunities.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://www.freelancersshow.com/&quot;&gt;The Freelancers' Show&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;A podcast on everything freelancing. Started as the Ruby Freelancers but the topics always were general. Fun to listen to, when it comes to software development you might also want to listen to the &lt;a href=&quot;http://rubyrogues.com/&quot;&gt;Ruby Rogues&lt;/a&gt;.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://www.bookyourselfsolid.com/&quot;&gt;Book Yourself Solid&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;I read this when getting started with freelancing. Helps you with deciding what you want to do and with marketing yourself.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://getclientsnow.com/&quot;&gt;Get Clients Now&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;A workbook with daily tasks for improving your business. It's a 28 day program that contains some really good ideas and helps you working on getting more work.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://www.ducttapemarketing.com/&quot;&gt;Duct Tape Marketing&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;A book on improving your marketing activities. I took less out of this book than the other two.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://theadmin.org/&quot;&gt;Email Course by Eric Davis&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;Eric Davis, one of the hosts of the Freelancers' Show also provides a free email course for freelancers.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://www.mediafon.net/ratgeber.php3&quot;&gt;Mediafon Ratgeber Selbstständige&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;A German book on all practical issues you have to take care of.&lt;/dd&gt;&lt;/dl&gt; &lt;p&gt;There is also stuff that is only slightly related to freelancing but helped me on the way, either through learning or motivation.&lt;/p&gt; &lt;dl&gt;&lt;dt&gt;&lt;a href=&quot;http://pragprog.com/book/actb/technical-blogging&quot;&gt;Technical Blogging&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;A book that can help you getting started with blogging. Can be motivating but also contains some good tips.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://www.amazon.com/Traffic-Simple-Visitors-without-Working-ebook/dp/B009WUKTXW/ref=la_B0098NFKNM_1_9?s=books&amp;ie=UTF8&amp;qid=1407898046&amp;sr=1-9&quot;&gt;My Blog Traffic Sucks.&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;A short book on blogging. This book lead to my very frequent blog publishing habit.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://scottberkun.com/books/&quot;&gt;Confessions of a Public Speaker&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;A very good and entertaining read on presenting.&lt;/dd&gt;&lt;dt&gt;&lt;a href=&quot;http://100startup.com/&quot;&gt;The 100$ Startup&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;Not exactly about freelancing but about small startups of all kinds, a very entertaining read about people working for themselves.&lt;/dd&gt;&lt;/dl&gt; &lt;p&gt;I am sure I forgot some of the things that helped me but I hope one of the resources can help you and your freelancing business. If you are missing something feel free to leave a comment.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Scrapy and Elasticsearch</title>
   <link href="http://blog.florian-hopf.de/2014/07/scrapy-and-elasticsearch.html"/>
   <updated>2014-07-30T15:09:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/07/scrapy-and-elasticsearch</id>
   <content type="html">&lt;p&gt;&lt;i&gt;On 29.07.2014 I gave a talk at &lt;a href=&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/&quot;&gt;Search Meetup Karlsruhe&lt;/a&gt; on using Scrapy with Elasticsearch, the &lt;a href=&quot;http://www.florian-hopf.de/artikel-vortraege/scrapy-es-smka/#/&quot;&gt;slides are here&lt;/a&gt;. This post evolved from the talk and introduces you to web scraping and search with Scrapy and Elasticsearch.&lt;/i&gt;&lt;/p&gt; &lt;h4&gt;Web Crawling&lt;/h4&gt; &lt;p&gt;You might think that web crawling and scraping only is for search engines like Google and Bing. But a lot of companies are using it for different purposes: Price comparison, financial risk information and portals all need a way to get the data. And at least sometimes the way is to retrieve it through some public website. Besides these cases where the data is not in your hand it can also make sense if the data is aggregated already. For intranet and portal search engines it can be easier to just scrape the frontend instead of building data import facilities for different, sometimes even old systems.&lt;/p&gt; &lt;h4&gt;The Example&lt;/h4&gt; &lt;p&gt;In this post we are looking at a rather artificial example: Crawling the meetup.com page for recent meetups to make them available for search. Why artificial? Because &lt;a href=&quot;http://www.meetup.com/meetup_api/&quot;&gt;meetup.com has an API&lt;/a&gt; that provides all the data in a more convenient way. But imagine there is no other way and we would like to build a custom search on this information, probably by adding other event sites as well.&lt;/p&gt; &lt;p&gt;This is a part of the &lt;a href=&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/&quot;&gt;Search Meetup Karlsruhe&lt;/a&gt; page that displays the recent meetups.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-eFtSzluW0ww/U9comqA1cQI/AAAAAAAAAX0/S8rlV9-3euE/s1600/recent-meetups.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-eFtSzluW0ww/U9comqA1cQI/AAAAAAAAAX0/S8rlV9-3euE/s400/recent-meetups.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;We can see that there is already some information we are interested in like the title and the link to the meetup page.&lt;/p&gt; &lt;h4&gt;Roll Your Own?&lt;/h4&gt; &lt;p&gt;When deciding on doing web scraping you might be tempted to build it yourself using a script or some code. How hard can it be to fetch a website, parse its source and extract all links to follow?&lt;/p&gt; &lt;p&gt;For demoing some of the features of &lt;a href=&quot;http://akka.io&quot;&gt;Akka&lt;/a&gt; I have built &lt;a href=&quot;http://blog.florian-hopf.de/2012/08/getting-rid-of-synchronized-using-akka.html&quot;&gt;a simple web crawler&lt;/a&gt; that visits a website, follows all links and indexes the content in Lucene. While this is not a lot of code you will notice soon that it is not suited for real world uses: It is hammering the crawled page with as many requests as possible. There is no way to make it behave nicely by respecting the robots.txt. Additional processing of the content is too hard to add afterwards. All of this is enough to lean to a ready made solution.&lt;/p&gt; &lt;h4&gt;Scrapy&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://scrapy.org/&quot;&gt;Scrapy&lt;/a&gt; is a framework for building crawlers and process the extracted data. It is implemented in Python and does asynchronous, non-blocking networking. It is easily extendable, not only via the item pipeline the content flows through. Finally it already comes with lots of features that you might have to build yourself otherwise.&lt;/p&gt; &lt;p&gt;In Scrapy you implement a spider, that visits a certain page and extracts items from the content. The items then flow through the item pipeline and get dumped to a Feed Exporter that then writes the data to a file. At every stage of the process you can add custom logic.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-Wdf_M4MRX9s/U9cpQcvpUEI/AAAAAAAAAX8/uU4--EGIub4/s1600/architecture.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-Wdf_M4MRX9s/U9cpQcvpUEI/AAAAAAAAAX8/uU4--EGIub4/s400/architecture.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;This is a very simplified diagram that doesn't take the asynchronous nature of Scrapy into account. See the Scrapy documentation for &lt;a href=&quot;http://doc.scrapy.org/en/latest/topics/architecture.html&quot;&gt;a more detailed view&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;For installing Scrapy I am using &lt;a href=&quot;https://pypi.python.org/pypi/pip&quot;&gt;pip&lt;/a&gt; which should be available for all systems. You can then run &lt;code&gt;pip install scrapy&lt;/code&gt; to get it.&lt;/p&gt; &lt;p&gt;To get started using Scrapy you can use the scaffolding feature to create a new project. Just issue something like &lt;code&gt;scrapy startproject meetup&lt;/code&gt; and scrapy will generate quite some files for you.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;meetup/&lt;br /&gt;meetup/scrapy.cfg&lt;br /&gt;meetup/meetup&lt;br /&gt;meetup/meetup/settings.py&lt;br /&gt;meetup/meetup/__init__.py&lt;br /&gt;meetup/meetup/items.py&lt;br /&gt;meetup/meetup/pipelines.py&lt;br /&gt;meetup/meetup/spiders&lt;br /&gt;meetup/meetup/spiders/__init__.py&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;For now we can concentrate on the &lt;code&gt;items.py&lt;/code&gt;, that describes the strucure of the data to crawl, and the &lt;code&gt;spiders&lt;/code&gt; directory where we can put our spiders.&lt;/p&gt;  &lt;h4&gt;Our First Spider&lt;/h4&gt; &lt;p&gt;First we need to define what data structure we would like to retrieve. This is described as an Item that is then created using a Spider and flows through the item pipeline. For our case we can put this into &lt;code&gt;items.py&lt;/code&gt;&lt;/p&gt; &lt;pre&gt;&lt;code&gt;from scrapy.item import Item, Field&lt;br /&gt;&lt;br /&gt;class MeetupItem(Item):&lt;br /&gt;    title = Field()&lt;br /&gt;    link = Field()&lt;br /&gt;    description = Field()&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Our &lt;code&gt;MeetupItem&lt;/code&gt; defines three fields for the title, the link and a description we can search on. For real world usecases this would contain more information like the date and time or probably more information on the participants.&lt;/p&gt; &lt;p&gt;To fetch data and create Items we need to implement a Spider instance. We create a file &lt;code&gt;meetup_spider.py&lt;/code&gt; in the &lt;code&gt;spiders&lt;/code&gt; directory.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;from scrapy.spider import BaseSpider&lt;br /&gt;from scrapy.selector import Selector&lt;br /&gt;from meetup.items import MeetupItem&lt;br /&gt;&lt;br /&gt;class MeetupSpider(BaseSpider):&lt;br /&gt;    name = &quot;meetup&quot;&lt;br /&gt;    allowed_domains = [&quot;meetup.com&quot;]&lt;br /&gt;    start_urls = [&lt;br /&gt;        &quot;http://www.meetup.com/Search-Meetup-Karlsruhe/&quot;&lt;br /&gt;    ]&lt;br /&gt;&lt;br /&gt;    def parse(self, response):&lt;br /&gt;        responseSelector = Selector(response)&lt;br /&gt;        for sel in responseSelector.css('li.past.line.event-item'):&lt;br /&gt;            item = MeetupItem()&lt;br /&gt;            item['title'] = sel.css('a.event-title::text').extract()&lt;br /&gt;            item['link'] = sel.xpath('a/@href').extract()&lt;br /&gt;            yield item&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Our spider extends BaseSpider and defines a name, the allowed domains and a start url. Scrapy calls the start url and passes the response to the &lt;code&gt;parse&lt;/code&gt; method. We are then using a &lt;code&gt;Selector&lt;/code&gt; to parse the data using eiher css or xpath. Both is shown in the example above.&lt;/p&gt; &lt;p&gt;Every Item we create is returned from the method. If we would have to visit another page we could also return a &lt;code&gt;Request&lt;/code&gt; object and Scrapy would then visit that page as well.&lt;/p&gt; &lt;p&gt;We can run this spider from the project directory by issuing &lt;code&gt;scrapy crawl meetup -o talks.json&lt;/code&gt;. This will use our meetup spider and write the items as JSON to a file.&lt;/p&gt;   &lt;pre&gt;&lt;code&gt;2014-07-24 18:27:59+0200 [scrapy] INFO: Scrapy 0.20.0 started (bot: meetup)&lt;br /&gt;[...]&lt;br /&gt;2014-07-24 18:28:00+0200 [meetup] DEBUG: Crawled (200) &amp;lt;get http:=&amp;quot;&amp;quot; www.meetup.com=&amp;quot;&amp;quot; search-meetup-karlsruhe=&amp;quot;&amp;quot;&amp;gt; (referer: None)&lt;br /&gt;2014-07-24 18:28:00+0200 [meetup] DEBUG: Scraped from &amp;lt;200 http://www.meetup.com/Search-Meetup-Karlsruhe/&amp;gt;&lt;br /&gt;        {&amp;#39;link&amp;#39;: [u&amp;#39;http://www.meetup.com/Search-Meetup-Karlsruhe/events/178746832/&amp;#39;],&lt;br /&gt;         &amp;#39;title&amp;#39;: [u&amp;#39;Neues in Elasticsearch 1.1 und Logstash in der Praxis&amp;#39;]}&lt;br /&gt;2014-07-24 18:28:00+0200 [meetup] DEBUG: Scraped from &amp;lt;200 http://www.meetup.com/Search-Meetup-Karlsruhe/&amp;gt;&lt;br /&gt;        {&amp;#39;link&amp;#39;: [u&amp;#39;http://www.meetup.com/Search-Meetup-Karlsruhe/events/161417512/&amp;#39;],&lt;br /&gt;         &amp;#39;title&amp;#39;: [u&amp;#39;Erstes Treffen mit Kurzvortr\xe4gen&amp;#39;]}&lt;br /&gt;2014-07-24 18:28:00+0200 [meetup] INFO: Closing spider (finished)&lt;br /&gt;2014-07-24 18:28:00+0200 [meetup] INFO: Stored jsonlines feed (2 items) in: talks.json&lt;br /&gt;2014-07-24 18:28:00+0200 [meetup] INFO: Dumping Scrapy stats:&lt;br /&gt;        {&amp;#39;downloader/request_bytes&amp;#39;: 244,&lt;br /&gt;         &amp;#39;downloader/request_count&amp;#39;: 1,&lt;br /&gt;[...]&lt;br /&gt;         &amp;#39;start_time&amp;#39;: datetime.datetime(2014, 7, 24, 16, 27, 59, 540300)}&lt;br /&gt;2014-07-24 18:28:00+0200 [meetup] INFO: Spider closed (finished)&lt;br /&gt;                    &amp;lt;/get&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can see that Scrapy visited the page and extracted two items. Finally it prints some stats on the crawl. The file contains our items as well&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&quot;link&quot;: [&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/events/178746832/&quot;], &quot;title&quot;: [&quot;Neues in Elasticsearch 1.1 und Logstash in der Praxis&quot;]}&lt;br /&gt;{&quot;link&quot;: [&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/events/161417512/&quot;], &quot;title&quot;: [&quot;Erstes Treffen mit Kurzvortr\u00e4gen&quot;]}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This is fine but there is a problem. We don't have all the data that we would like to have, we are missing the description. This information is not fully available on the overview page so we need to crawl the detail pages of the meetup as well.&lt;/p&gt; &lt;h4&gt;The Crawl Spider&lt;/h4&gt; &lt;p&gt;We still need to use our overview page because this is where all the recent meetups are listed. But for retrieving the item data we need to go to the &lt;a href=&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/events/178746832/&quot;&gt;detail page&lt;/a&gt;.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-pRkymnKrMz0/U9cp4kMCpxI/AAAAAAAAAYM/8O9C--JS78A/s1600/meetup-details.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-pRkymnKrMz0/U9cp4kMCpxI/AAAAAAAAAYM/8O9C--JS78A/s400/meetup-details.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;As mentioned already we could solve our new requirement using our spider above by returning Request objects and a new callback function. But we can solve it another way, by using the CrawlSpider that can be configured with a Rule that advices where to extract links to visit.&lt;/p&gt; &lt;p&gt;In case you are confused, welcome to the world of Scrapy! When working with Scrapy you will regularly find cases where there are several ways to do a thing.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;from scrapy.contrib.spiders import CrawlSpider, Rule&lt;br /&gt;from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor&lt;br /&gt;from scrapy.selector import Selector&lt;br /&gt;from meetup.items import MeetupItem&lt;br /&gt;&lt;br /&gt;class MeetupDetailSpider(CrawlSpider):&lt;br /&gt;    name = &quot;meetupDetail&quot;&lt;br /&gt;    allowed_domains = [&quot;meetup.com&quot;]&lt;br /&gt;    start_urls = [&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/&quot;]&lt;br /&gt;    rules = [Rule(SgmlLinkExtractor(restrict_xpaths=('//div[@id=&quot;recentMeetups&quot;]//a[@class=&quot;event-title&quot;]')), callback='parse_meetup')]&lt;br /&gt;&lt;br /&gt;    def parse_meetup(self, response):&lt;br /&gt;        sel = Selector(response)&lt;br /&gt;        item = MeetupItem()&lt;br /&gt;        item['title'] = sel.xpath('//h1[@itemprop=&quot;name&quot;]/text()').extract()&lt;br /&gt;        item['link'] = response.url&lt;br /&gt;        item['description'] = sel.xpath('//div[@id=&quot;past-event-description-wrap&quot;]//text()').extract()&lt;br /&gt;        yield item&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Besides the information we have set for our other spider we now also add a &lt;code&gt;Rule&lt;/code&gt; object. It extracts the links from the list and passes the responses to the supplied callback. You can also add rules that visit links by path, e.g. all with the fragment &lt;code&gt;/articles/&lt;/code&gt; in the url.&lt;/p&gt; &lt;p&gt;Our &lt;code&gt;parse_meetup&lt;/code&gt; method now doesn't work on the overview page but on the detail pages that are extracted by the rule. The detail page has all the information available we need and will now even pass the description to our item.&lt;/p&gt; &lt;p&gt;Now that we have all the information we can do something useful with it: Index it in Elasticsearch.&lt;/p&gt; &lt;h4&gt;Elasticsearch&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/noplay/scrapy-elasticsearch&quot;&gt;Elasticsearch support for Scrapy&lt;/a&gt; is available by installing a module: &lt;code&gt;pip install &quot;ScrapyElasticSearch&quot;&lt;/code&gt;. It takes the Items created by your spider and indexes those in Elasticsearch using the library &lt;a href=&quot;https://github.com/aparo/pyes&quot;&gt;pyes&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Looking at the Scrapy architecture above you might expect that the module is implemented as a FeedExporter that exports the items to Elasticsearch instead of the filesystem. For reasons unknown to me exporting to a database or search engine is done using an &lt;code&gt;ItemPipeline&lt;/code&gt; which is a component in the item pipeline. Confused?&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-XpjHwPV0L3w/U9cpv6Bt_rI/AAAAAAAAAYE/M20V-YJPZ0E/s1600/architecture-es.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-XpjHwPV0L3w/U9cpv6Bt_rI/AAAAAAAAAYE/M20V-YJPZ0E/s400/architecture-es.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;To configure Scrapy to put the items to Elasticsearch of course you need to have an instance running somewhere. The pipeline is configured in the file &lt;code&gt;settings.py&lt;/code&gt;.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;ITEM_PIPELINES = [&lt;br /&gt;  'scrapyelasticsearch.ElasticSearchPipeline',&lt;br /&gt;]&lt;br /&gt;&lt;br /&gt;ELASTICSEARCH_SERVER = 'localhost' &lt;br /&gt;ELASTICSEARCH_PORT = 9200 &lt;br /&gt;ELASTICSEARCH_INDEX = 'meetups'&lt;br /&gt;ELASTICSEARCH_TYPE = 'meetup'&lt;br /&gt;ELASTICSEARCH_UNIQ_KEY = 'link'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The configuration should be straightforward. We enable the module by adding it to the &lt;code&gt;ITEM_PIPELINES&lt;/code&gt; and configure additional information like the host, index and type name. Now when crawling for the next time Scrapy will automatically push your data to Elasticsearch.&lt;/p&gt; &lt;p&gt;I am not sure if this can be an issue when it comes to crawling but the module doesn't use bulk indexing but indexes each item by itself. If you have a very large amount of data this could be a problem but should be totally fine for most uses. Also, of course you need to make sure that your mapping is in place before indexing data if you need some predefined handling.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;I hope you could see how useful Scrapy can be and how easy it is to put the data in stores like Elasticsearch. Some approaches of Scrapy can be quite confusing at first but nevertheless it's an extremely useful tool. Give it a try the next time you are thinking about web scraping.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Use Cases for Elasticsearch: Flexible Query Cache</title>
   <link href="http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-flexible.html"/>
   <updated>2014-07-25T14:58:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/07/use-cases-for-elasticsearch-flexible</id>
   <content type="html">&lt;p&gt;In the previous two posts on use cases for Elasticsearch we have seen that Elasticsearch &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-document.html&quot;&gt;can be used to store even large amounts of documents&lt;/a&gt; and that we can &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-full-text.html&quot;&gt;access those using the full text features of Lucene via the Query DSL&lt;/a&gt;. In this shorter post we will put both of use cases together to see how read heavy applications can benefit from Elasticsearch.&lt;/p&gt; &lt;h4&gt;Search Engines in Classic Applications&lt;/h4&gt; &lt;p&gt;Looking at classic applications search engines were a specialized thing that was only responsible for helping with one feature, the search page.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-Ntzwo4msGVg/U9H9E8IG4HI/AAAAAAAAAXU/Wsw2dvj-thQ/s1600/search-page-setup.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-Ntzwo4msGVg/U9H9E8IG4HI/AAAAAAAAAXU/Wsw2dvj-thQ/s400/search-page-setup.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;On the left we can see our application, most of its functionality is build by querying the database. The search engine only plays a minor part and is responsible for rendering the search page.&lt;/p&gt; &lt;p&gt;Databases are well suited for lots of types of applications but it turns out that often it is not that easy to scale them. Websites with high traffic peaks often have some problems scaling database access. Indexing and scaling machines up can help but often requires specialized knowledge and can become rather expensive.&lt;/p&gt; &lt;p&gt;As with other search features especially ecommerce providers started doing something different. They started to employ the search engine not only for full text search but also for other parts of the page that require no direct keyword input by the user. Again, let's have a look at a page at Amazon.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-7eG5ZHhOWhw/U9H9dqVkwFI/AAAAAAAAAXc/IPI-zwg2utY/s1600/amazon-cache.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-7eG5ZHhOWhw/U9H9dqVkwFI/AAAAAAAAAXc/IPI-zwg2utY/s400/amazon-cache.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;This is one of the category pages that can be accessed using the navigation. We can already see that the interface looks very similar to a search result page. There is a result list, we can sort and filter the results using the facets. Though of course I have no insight how Amazon is doing this exactly a common approach is to use the search engine for pages like this as well.&lt;/p&gt; &lt;h4&gt;Scaling Read Requests&lt;/h4&gt; &lt;p&gt;A common problem for ecommerce websites is that there are huge traffic spikes. Depending on your kind of business you might have a lot more traffic just before christmas. Or you might have to fight spikes when there are TV commercials for your service or any special discounts. Flash sale sites are at the extreme end of those kind of sites with very high spikes at a certain point in time when a sale starts.&lt;/p&gt; &lt;p&gt;It turns out that search engines are good at being queried a lot. The immutable data set, the segments, are very cache friendly. When it comes to filters those can be cached by the engine as well most of the times. On a warm index most of the data will be in RAM so it is lightning fast.&lt;/p&gt; &lt;p&gt;Back to our example of talks that can be accessed online. Imagine a navigation where the user can choose the city she wants to see events for. You can then issue a query like this to Elasticsearch:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/conferences/_search &quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;filter&quot;: {&lt;br /&gt;        &quot;term&quot;: {&lt;br /&gt;           &quot;conference.city&quot;: &quot;stuttgart&quot;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;There is no query part but only a filter that limits the results to the talks that are in Stuttgart. The whole filter will be cached so if a lot of users are accessing the data there can be a huge performance gain for you and especially your users.&lt;/p&gt; &lt;p&gt;Additionally as we have seen new nodes can be added to Elasticsearch without a lot of hassle. If we need more query capacity we can easily add more machines and more replicas, even temporarily. When we can identify some pages that can be moved to the search engine the database doesn't need to have that much traffic anymore.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-Y3MwbD3X4rw/U9H9kv5VWPI/AAAAAAAAAXk/6fiuT4TFAF4/s1600/search-engine-as-cache.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-Y3MwbD3X4rw/U9H9kv5VWPI/AAAAAAAAAXk/6fiuT4TFAF4/s400/search-engine-as-cache.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Especially for getting the huge spikes under control it is best to try to not access the database anymore for read heavy pages and deliver all of the content from the search engine. &lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Though in this post we have looked at ecommerce the same strategy can be applied to different domains. Content management systems can push the editorial content to search engines and let those be responsible for scaling. Classifieds, social media aggregation, .... All of those can benefit from the cache friendly nature of a search engine. Maybe you will even notice that parts of your data don't need to be in the database at all and you can migrate them to Elasticsearch as a primary data store. A first step to polyglot persistence.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Slides for Use Cases for Elasticsearch for Developer Week and Java Forum Stuttgart</title>
   <link href="http://blog.florian-hopf.de/2014/07/slides-for-use-cases-for-elasticsearch.html"/>
   <updated>2014-07-16T00:06:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/07/slides-for-use-cases-for-elasticsearch</id>
   <content type="html">&lt;p&gt;I am giving the German talk &lt;em&gt;Anwendungsfälle für Elasticsearch (Use Cases for Elasticsearch)&lt;/em&gt; twice in July 2014, first at &lt;a href=&quot;http://developer-week.de&quot;&gt;Developer Week Nürnberg&lt;/a&gt; at 15.07.2014 and then at &lt;a href=&quot;http://java-forum-stuttgart.de&quot;&gt;Java Forum Stuttgart&lt;/a&gt; at 17.07.2014. The slides for the Developer Week talk, which are a superset of the Java Forum talk, are now &lt;a href=&quot;http://www.slideshare.net/fhopf/anwendungsfaelle-fr-elasticsearch&quot;&gt;available on Slideshare.&lt;/a&gt;&lt;/p&gt; &lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/37004850&quot; width=&quot;476&quot; height=&quot;400&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;&gt;&lt;/iframe&gt; &lt;p&gt;In additon to the talks I published a blog post on each of the use cases. &lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-document.html&quot;&gt;Elasticsearch as a Document Store&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-full-text.html&quot;&gt;Elasticsearch for Full Text Search&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-flexible.html&quot;&gt;Elasticsearch as a Flexible Cache&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.florian-hopf.de/2014/08/use-cases-for-elasticsearch-geospatial.html&quot;&gt;Geo Search with Elasticsearch&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.florian-hopf.de/2014/09/use-cases-for-elasticsearch-index-and.html&quot;&gt;Insight in your Logfiles with Logstash, Elasticsearch and Kibana&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blog.florian-hopf.de/2014/12/use-cases-for-elasticsearch-analytics.html&quot;&gt;Analytics with Elasticsearch&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;If you are interested in all posts on this blog you can &lt;a href=&quot;http://feeds.feedburner.com/florian-hopf/UjyC&quot;&gt;subscribe to my feed&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;If you have any feedback on the talk or the topic I would appreciate a comment or you can just &lt;a href=&quot;http://www.florian-hopf.de/kontakt/&quot;&gt;contact me directly&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Use Cases for Elasticsearch: Full Text Search</title>
   <link href="http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-full-text.html"/>
   <updated>2014-07-11T15:09:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/07/use-cases-for-elasticsearch-full-text</id>
   <content type="html">&lt;p&gt;In the last post of this series on use cases for Elasticsearch we looked at &lt;a href=&quot;http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-document.html&quot;&gt;the features Elasticsearch provides for storing even large amounts of documents&lt;/a&gt;. In this post we will look at another one of its core features: Search. I am building on some of the information in the previous post so if you haven't read it you should do so now.&lt;/p&gt; &lt;p&gt;As we have seen we can use Elasticsearch to store JSON documents that can even be distributed across several machine. Indexes are used to group documents and each document is stored using a certain type. Shards are used to distribute parts of an index across several nodes and replicas are copies of shards that are used for distributing load as well as for fault tolerance.&lt;/p&gt; &lt;h4&gt;Full Text Search&lt;/h4&gt; &lt;p&gt;Everybody uses full text search. The amount of information has just become too much to access it using navigation and categories alone. &lt;a href=&quot;http://google.com&quot;&gt;Google&lt;/a&gt; is the most prominent example offering instant keyword search across a huge amount of information.&lt;/p&gt;   &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-t_qI3AuKs50/U79mc923AEI/AAAAAAAAAWo/VNGt2ogVhHM/s1600/google.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-t_qI3AuKs50/U79mc923AEI/AAAAAAAAAWo/VNGt2ogVhHM/s640/google.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Looking at what Google does we can already see some common features of full text search. Users only provide keywords and expect the search engine to provide good results. Relevancy of documents is expected to be good and users want the results they are looking for on the first page. How relevant a document is can be influenced by different factors like h the queried term exists in a document. Besides getting the best results the user wants to be supported during the search process. Features like suggestions and highlighting on the result excerpt can help with this.&lt;/p&gt; &lt;p&gt;Another area where search is important is E-Commerce with &lt;a href=&quot;http://amazon.com&quot;&gt;Amazon&lt;/a&gt; being one of the dominant players.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-AC2aU4dw7a8/U79mukxK8MI/AAAAAAAAAWw/vollx2QzZr4/s1600/amazon.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-AC2aU4dw7a8/U79mukxK8MI/AAAAAAAAAWw/vollx2QzZr4/s640/amazon.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The interface looks similar to the Google one. The user can enter keywords that are then searched for. But there are also slight differences. The suggestions Amazon provides are more advanced, also hinting at categories a term might be found in. Also the result display is different, consisting of a more structured view. The structure of the documents being searched is also used for determining the facets on the left that can be used to filter the current result based on certain criteria, e.g. all results that cost between 10 and 20 €. Finally, relevance might mean something completely different when it comes to something like an online store. Often the order of the result listing is influenced by the vendor or the user can sort the results by criteria like price or release date.&lt;/p&gt; &lt;p&gt;Though neither Google nor Amazon are using Elasticsearch you can use it to build similar solutions.&lt;/p&gt; &lt;h4&gt;Searching in Elasticsearch&lt;/h4&gt; &lt;p&gt;As with everything else, Elasticsearch can be searched using HTTP. In the most simple case you can &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-uri-request.html&quot;&gt;append the _search endpoint to the url and add a parameter&lt;/a&gt;: &lt;code&gt;curl -XGET &quot;http://localhost:9200/conferences/talk/_search?q=elasticsearch&amp;pretty=true&quot;&lt;/code&gt;. Elasticsearch will then respond with the results, ordered by relevancy.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt;  &quot;took&quot; : 81,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 1,&lt;br /&gt;    &quot;max_score&quot; : 0.067124054,&lt;br /&gt;    &quot;hits&quot; : [ {&lt;br /&gt;      &quot;_index&quot; : &quot;conferences&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;talk&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;iqxb7rDoTj64aiJg55KEvA&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.067124054,&lt;br /&gt;      &quot;_source&quot;:{&lt;br /&gt;    &quot;title&quot; : &quot;Anwendungsfälle für Elasticsearch&quot;,&lt;br /&gt;    &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;    &quot;date&quot; : &quot;2014-07-17T15:35:00.000Z&quot;,&lt;br /&gt;    &quot;tags&quot; : [&quot;Java&quot;, &quot;Lucene&quot;],                                  &lt;br /&gt;    &quot;conference&quot; : {&lt;br /&gt;        &quot;name&quot; : &quot;Java Forum Stuttgart&quot;,&lt;br /&gt;        &quot;city&quot; : &quot;Stuttgart&quot;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Though we have searched on a certain type now you can also search multiple types or multiple indices.&lt;/p&gt; &lt;p&gt;Adding a parameter is easy but search requests can become more complex. We might request highlighting or filter the documents according to a criteria. Instead of using parameters for everything Elasticsearch offers the so called &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html&quot;&gt;Query DSL&lt;/a&gt;, a search API that is passed in the body of the request and is expressed using JSON.&lt;/p&gt; &lt;p&gt;This query could be the result of a user trying to search for elasticsearch but mistyping parts of it. The results are filtered so that only talks for conferences in the city of Stuttgart are returned.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/conferences/_search &quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;query&quot;: {&lt;br /&gt;        &quot;match&quot;: {&lt;br /&gt;            &quot;title&quot; : {&lt;br /&gt;               &quot;query&quot;: &quot;elasticsaerch&quot;,&lt;br /&gt;               &quot;fuzziness&quot;: 2&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    },&lt;br /&gt;    &quot;filter&quot;: {&lt;br /&gt;        &quot;term&quot;: {&lt;br /&gt;           &quot;conference.city&quot;: &quot;stuttgart&quot;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This time we are querying all documents of all types in the index conferences. The query object requests one of the common queries, a &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-match-query.html&quot;&gt;match query&lt;/a&gt; on the title field of the document. The query attribute contains the search term that would be passed in by the user. The fuzziness attribute requests that we should also find documents that contain terms that are similar to the term requested. This will take care of the misspelled term and also return results containing elasticsearch. The filter object requests that all results should be filtered according to the city of the conference. Filters should be used whenever possible as they can be cached and do not calculate the relevancy which should make them faster.&lt;/p&gt; &lt;h4&gt;Normalizing Text&lt;/h4&gt; &lt;p&gt;As search is used everywhere users also have some expectations of how it should work. Instead of issuing exact keyword matches they might use terms that are only similar to the ones that are in the document. For example a user might be querying for the term Anwendungsfall which is the singular of the contained term Anwendungsfälle, meaning use cases in German: &lt;code&gt;curl -XGET &quot;http://localhost:9200/conferences/talk/_search?q=title:anwendungsfall&amp;pretty=true&quot;&lt;/code&gt;&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt;  &quot;took&quot; : 2,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 0,&lt;br /&gt;    &quot;max_score&quot; : null,&lt;br /&gt;    &quot;hits&quot; : [ ]&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;No results. We could try to solve this using the fuzzy search we have seen above but there is a better way. We can normalize the text during indexing so that both keywords point to the same term in the document.&lt;/p&gt; &lt;p&gt;Lucene, the library search and storage in Elasticsearch is implemented with provides the underlying data structure for search, the inverted index. Terms are mapped to the documents they are contained in. A process called analyzing is used to split the incoming text and add, remove or modify terms.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-HrdoXgCeWV0/U79uZK9FZEI/AAAAAAAAAXA/Ki9QbCm6EBY/s1600/analyzing.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-HrdoXgCeWV0/U79uZK9FZEI/AAAAAAAAAXA/Ki9QbCm6EBY/s640/analyzing.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;On the left we can see two documents that are indexed, on the right we can see the inverted index that maps terms to the documents they are contained in. During the analyzing process the content of the documents is split and transformed in an application specific way so it can be put in the index. Here the text is first split on whitespace or punctuation. Then all the characters are lowercased. In a final step the language dependent stemming is employed that tries to find the base form of terms. This is what transforms our Anwendungsfälle to Anwendungsfall.&lt;/p&gt; &lt;p&gt;What kind of logic is executed during analyzing depends on the data of your application. The analyzing process is one of the main factors for determining the quality of your search and you can spend quite some time with it. For more details you might want to look at my post on &lt;a href=&quot;http://blog.florian-hopf.de/2014/04/the-absolute-basics-of-indexing-data.html&quot;&gt;the absolute basics of indexing data.&lt;/a&gt;&lt;/p&gt; &lt;p&gt;In Elasticsearch, how fields are analyzed is determined by the mapping of the type. Last week we have seen that we can index documents of different structure in Elasticsearch but as we can see now Elasticsearch is not exactly schema free. The analyzing process for a certain field is determined once and cannot be changed easily. You can add additional fields but you normally don't change how existing fields are stored.&lt;/p&gt; &lt;p&gt;If you don't supply a mapping Elasticsearch will do some educated guessing for the documents you are indexing. It will look at any new field it sees during indexing and do what it thinks is best. In the case of our title it uses the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis-standard-analyzer.html&quot;&gt;StandardAnalyzer&lt;/a&gt; because it is a string. Elasticsearch does not know what language our string is in so it doesn't do any stemming which is a good default.&lt;/p&gt; &lt;p&gt;To tell Elasticsearch to use the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html#german-analyzer&quot;&gt;GermanAnalyzer&lt;/a&gt; instead we need to add a custom mapping. We first delete the index and create it again:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XDELETE &quot;http://localhost:9200/conferences/&quot;&lt;br /&gt;&lt;br /&gt;curl -XPUT &quot;http://localhost:9200/conferences/“&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We can then use the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-put-mapping.html&quot;&gt;PUT mapping API&lt;/a&gt; to pass in the mapping for our type.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPUT &quot;http://localhost:9200/conferences/talk/_mapping&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;properties&quot;: {&lt;br /&gt;       &quot;tags&quot;: {&lt;br /&gt;          &quot;type&quot;: &quot;string&quot;,&lt;br /&gt;          &quot;index&quot;: &quot;not_analyzed&quot;&lt;br /&gt;       },&lt;br /&gt;       &quot;title&quot;: {&lt;br /&gt;          &quot;type&quot;: &quot;string&quot;,&lt;br /&gt;          &quot;analyzer&quot;: &quot;german&quot;&lt;br /&gt;       }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We have only supplied a custom mapping for two fields. The rest of the fields will again be guessed by Elasticsearch. When creating a production app you will most likely map all of your fields in advance but the ones that are not that relevant can also be mapped automatically. Now, if we index the document again and search for the singular, the document will be found.&lt;/p&gt; &lt;h4&gt;Advanced Search&lt;/h4&gt; &lt;p&gt;Besides the features we have seen here Elasticsearch provides a lot more. You can automatically gather facets for the results using aggregations which we will look at in a later post. The suggesters can be used to perform autosuggestion for the user, terms can be highlighted, results can be sorted according to fields, you get pagination with each request, .... As Elasticsearch builds on Lucene all the goodies for building an advanced search application are available.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Search is a core part of Elasticsearch that can be combined with its distributed storage capabilities. You can use to Query DSL to build expressive queries. Analyzing is a core part of search and can be influenced by adding a custom mapping for a type. Lucene and Elasticsearch provide lots of advanced features for adding search to your application.&lt;/p&gt; &lt;p&gt;Of course there are lots of users that are building on Elasticsearch because of its search features and its distributed nature. &lt;a href=&quot;http://exploringelasticsearch.com/github_interview.html&quot;&gt;GitHub uses it to let users search the repositories&lt;/a&gt;, &lt;a href=&quot;http://meta.stackexchange.com/questions/160100/a-new-search-engine-for-stack-exchange&quot;&gt;StackOverflow indexes all of its questions and answers in Elasticsearch&lt;/a&gt; and &lt;a href=&quot;http://developers.soundcloud.com/blog/architecture-behind-our-new-search-and-explore-experience&quot;&gt;SoundCloud offers search in the metadata of the songs.&lt;/a&gt;&lt;/p&gt; &lt;p&gt;In the next post we will look at another aspect of Elasticsearch: Using it to index geodata, which lets you filter and sort results by postition and distance.&lt;/p&gt;  </content>
 </entry>
 
 <entry>
   <title>Use Cases for Elasticsearch: Document Store</title>
   <link href="http://blog.florian-hopf.de/2014/07/use-cases-for-elasticsearch-document.html"/>
   <updated>2014-07-04T19:28:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/07/use-cases-for-elasticsearch-document</id>
   <content type="html">&lt;p&gt;&lt;i&gt;I'll be giving an introductory talk about Elasticsearch twice in July, first at &lt;a href=&quot;www.developer-week.de&quot;&gt;Developer Week Nürnberg&lt;/a&gt;, then at &lt;a href=&quot;http://www.java-forum-stuttgart.de/de/Home.html&quot;&gt;Java Forum Stuttgart&lt;/a&gt;. I am showing some of the features of Elasticsearch by looking at certain use cases. To prepare for the talks I will try to describe each of the use cases in a blog post as well.&lt;/i&gt;&lt;/p&gt; &lt;p&gt;When it comes to Elasticsearch the first thing to look at often is the search part. But in this post I would like to start with its capabilities as a distributed document store.&lt;/p&gt; &lt;h4&gt;Getting Started&lt;/h4&gt; &lt;p&gt;Before we start we need to install Elasticsearch which fortunately is very easy. You can just download the archive, unpack it and use a script to start it. As it is a Java based application you of course need to have a Java runtime installed.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;# download archive&lt;br /&gt;wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.2.1.zip&lt;br /&gt; &lt;br /&gt;# zip is for windows and linux&lt;br /&gt;unzip elasticsearch-1.2.1.zip&lt;br /&gt; &lt;br /&gt;# on windows: elasticsearch.bat&lt;br /&gt;elasticsearch-1.2.1/bin/elasticsearch&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Elasticsearch can be talked to using HTTP and JSON.When looking around at examples you will often see curl being used because it is widely available. (See &lt;a href=&quot;http://blog.florian-hopf.de/2014/06/goodbye-sense-welcome-alternatives.html&quot;&gt;this post on querying Elasticsearch using plugins&lt;/a&gt; for alternatives). To see if it is up and running you can issue a GET request on port 9200: &lt;code&gt;curl -XGET http://localhost:9200&lt;/code&gt;. If everything is set up correctly Elasticsearch will respond with something like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt; &quot;status&quot; : 200,&quot;name&quot; : &quot;Hawkeye&quot;, &lt;br /&gt; &quot;version&quot; : {&lt;br /&gt;  &quot;number&quot; : &quot;1.2.1&quot;,&lt;br /&gt;  &quot;build_hash&quot; : &quot;6c95b759f9e7ef0f8e17f77d850da43ce8a4b364&quot;,&lt;br /&gt;  &quot;build_timestamp&quot; : &quot;2014-06-03T15:02:52Z&quot;,&lt;br /&gt;     &quot;build_snapshot&quot; : false,&lt;br /&gt;     &quot;lucene_version&quot; : &quot;4.8&quot;&lt;br /&gt;  },&lt;br /&gt;  &quot;tagline&quot; : &quot;You Know, for Search&quot;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;Storing Documents&lt;/h4&gt; &lt;p&gt;When I say document this means two things. First, Elasticsearch stores JSON documents and even uses JSON internally a lot. This is an example of a simple document that describes talks for conferences.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt;    &quot;title&quot; : &quot;Anwendungsfälle für Elasticsearch&quot;,&lt;br /&gt;    &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;    &quot;date&quot; : &quot;2014-07-17T15:35:00.000Z&quot;,&lt;br /&gt;    &quot;tags&quot; : [&quot;Java&quot;, &quot;Lucene&quot;],&lt;br /&gt;    &quot;conference&quot; : {&lt;br /&gt;        &quot;name&quot; : &quot;Java Forum Stuttgart&quot;,&lt;br /&gt;        &quot;city&quot; : &quot;Stuttgart&quot;&lt;br /&gt;    } &lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;There are fields and values, arrays and nested documents. Each of those features is supported by Elasticsearch.&lt;/p&gt; &lt;p&gt;Besides the JSON documents that are used for storing data in Elasticsearch, document refers to the underlying library Lucene, that is used to persist the data and handles data as documents consisting of fields. So this is a perfect match: Elasticsearch uses JSON, which is very popular and supported from lots of technologies. But the underlying data structures also use documents.&lt;/p&gt; &lt;p&gt;When indexing a document we can issue a post request to a certain URL. The body of the request contains the document to be stored, the file we are passing contains the content we have seen above.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST http://localhost:9200/conferences/talk/ --data-binary @talk-example-jfs.json&lt;/pre&gt;&lt;/code&gt; &lt;p&gt;When started Elasticsearch listens on port 9200 by default. For storing information we need to provide some additional information in the URL. The first segment after the port is the index name. An index name is a logical grouping of documents. If you want to compare it to the relational world this can be thought of as the database.&lt;/p&gt; &lt;p&gt;The next segment that needs to be provided is the type. A type can describe the structure of the doucments that are stored in it. You can again compare this to the relational world, this could be a table, but that is only slightly correct. Documents of any kind can be stored in Elasticsearch, that is why it is often called schema free. We will look at this behaviour in the next post where you will see that schema free isn't the most appropriate term for it. For now it is enough to know that you can store documents with completely different structure in Elasticsearch. This also means you can evolve your documents and add new fields as appropriate.&lt;/p&gt; &lt;p&gt;Note that neither index nor type need to exist when starting indexing documents. They will be created automatically, one of the many features that makes it so easy to start with Elasticsearch.&lt;/p&gt; &lt;p&gt;When you are storing a document in Elasticsearch it will automatically generate an id for you that is also returned in the result.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt; &quot;_index&quot;:&quot;conferences&quot;,&lt;br /&gt; &quot;_type&quot;:&quot;talk&quot;,&lt;br /&gt; &quot;_id&quot;:&quot;GqjY7l8sTxa3jLaFx67_aw&quot;,&lt;br /&gt; &quot;_version&quot;:1,&lt;br /&gt; &quot;created&quot;:true&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;In case you want to determine the id yourself you can also use a PUT on the same URL we have seen above plus the id. I don't want to get into trouble by calling this RESTful but did you notice that Elasticsearch makes good use of the HTTP verbs?&lt;/p&gt; &lt;p&gt;Either way how you stored the document you can always retrieve it by specifying the index, type and id.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET http://localhost:9200/conferences/talk/GqjY7l8sTxa3jLaFx67_aw?pretty=true&lt;/pre&gt;&lt;/code&gt; &lt;p&gt;which will respond with something like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt;  &quot;_index&quot; : &quot;conferences&quot;,&lt;br /&gt; [...]&lt;br /&gt;  &quot;_source&quot;:{&lt;br /&gt;    &quot;title&quot; : &quot;Anwendungsfälle für Elasticsearch&quot;,&lt;br /&gt;    &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;    &quot;date&quot; : &quot;2014-07-17T15:35:00.000Z&quot;,&lt;br /&gt;    &quot;tags&quot; : [&quot;Java&quot;, &quot;Lucene&quot;],&lt;br /&gt;    &quot;conference&quot; : {&lt;br /&gt;        &quot;name&quot; : &quot;Java Forum Stuttgart&quot;,&lt;br /&gt;        &quot;city&quot; : &quot;Stuttgart&quot;&lt;br /&gt;    } &lt;br /&gt;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can see that the source in the response contains exactly the document we have indexed before.&lt;/p&gt; &lt;h4&gt;Distributed Storage&lt;/h4&gt; &lt;p&gt;So far we have seen how Elasticsearch stores and retrieves documents and we have learned that you can evolve the schema of your documents. The huge benefit we haven't touched so far is that it is distributed. Each index can be split into several shards that can then be distributed across several machines.&lt;/p&gt; &lt;p&gt;To see the distributed nature in action fortunately we don't need several machines. First, let's see the state of our currently running instance in the plugin elasticsearch-kopf (See &lt;a href=&quot;http://blog.florian-hopf.de/2014/06/elasticsearch-kopf.html&quot;&gt;this post on details how to install and use it&lt;/a&gt;):&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-3wTGmr8OEpw/U7aLKubVJ_I/AAAAAAAAAV4/1b31wT16rnc/s1600/single-node-sharded.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-3wTGmr8OEpw/U7aLKubVJ_I/AAAAAAAAAV4/1b31wT16rnc/s640/single-node-sharded.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;On the left you can see that there is one machine running. The row on top shows that it contains our index conferences. Even though we didn't explicitly tell Elasticsearch it created 5 shards for our index that are currently all on the instance we started. As each of the shards is a Lucene index in itself even if you are running your index on one instance the documents you are storing are already distributed across several Lucene indexes.&lt;/p&gt;   &lt;p&gt;We can now use the same installation to start another node. After a short time we should see the instance in the dashboard as well.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-bXtZuHZKlwo/U7aLLxLAMQI/AAAAAAAAAWE/jnS9FOOKVJY/s1600/two-nodes.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-bXtZuHZKlwo/U7aLLxLAMQI/AAAAAAAAAWE/jnS9FOOKVJY/s640/two-nodes.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;As the new node joins the cluster (which by default happens automatically) Elasticsearch will automatically copy the shards to the new node. This is because by default it not only uses 5 shards but also 1 replica, which is a copy of a shard. Replicas are always placed on different nodes than their shards and are used for distributing the load and for fault tolerance. If one of the nodes crashes the data is still available on the other node.&lt;/p&gt; &lt;p&gt;Now, if we start another node something else will happen. Elasticsearch will rebalance the shards. It will copy and move shards to the new node so that the shards are distributed evenly across the machines.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-Y2n20QgMLyk/U7aLLylRedI/AAAAAAAAAWI/h43R9uNeI3M/s1600/three-nodes.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-Y2n20QgMLyk/U7aLLylRedI/AAAAAAAAAWI/h43R9uNeI3M/s640/three-nodes.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Once defined when creating an index the number of shards can't be changed. That's why you normally overallocate (create more shards than you need right now) or if your data allows it you can create time based indices. Just be aware that sharding comes with some cost and think carefully about what you need. Designing your distribution setup can still be difficult even with Elasticsearch does a lot for you out of the box.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;In this post we have seen how easy it is to store and retrieve documents using Elasticsearch. JSON and HTTP are technologies that are available in lots of programming environments. The schema of your documents can be evolved as your requirements change. Elasticsearch distributes the data by default and lets you scale across several machines so it is suited well even for very large data sets.&lt;/p&gt; &lt;p&gt;Though using Elasticsearch as a document store is a real use case it is hard to find users that are only using it that way. Nobody retrieves the documents only by id as we have seen in this post but uses the rich query facilities we will look at next week. Nevertheless you can read about how &lt;a href=&quot;http://highscalability.com/blog/2014/1/6/how-hipchat-stores-and-indexes-billions-of-messages-using-el.html&quot;&gt;Hipchat uses Elasticsearch to store billions of messages&lt;/a&gt; and how &lt;a href=&quot;http://www.elasticsearch.org/case-study/engagor/&quot;&gt;Engagor uses Elasticsearch here&lt;/a&gt; and &lt;a href=&quot;http://www.jurriaanpersyn.com/archives/2013/11/18/introduction-to-elasticsearch/&quot;&gt;here&lt;/a&gt;. Both of them are using Elasticsearch as their primary storage.&lt;/p&gt; &lt;p&gt;Though it sounds more drastic than it probably is: If you are considering using Elasticsearch as your primary storage you should also read &lt;a href=&quot;http://aphyr.com/posts/317-call-me-maybe-elasticsearch&quot;&gt;this analysis of Elasticsearchs behaviour in case of network partitions&lt;/a&gt;. Next week we will be looking at using Elasticsearch for something obvious: Search.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Goodbye Sense - Welcome Alternatives?</title>
   <link href="http://blog.florian-hopf.de/2014/06/goodbye-sense-welcome-alternatives.html"/>
   <updated>2014-06-27T15:28:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/06/goodbye-sense-welcome-alternatives</id>
   <content type="html">&lt;p&gt;I only recently noticed that &lt;a href=&quot;https://www.found.no/foundation/Sense-Elasticsearch-interface/&quot;&gt;Sense, the Chrome Plugin for Elasticsearch&lt;/a&gt; has been &lt;a href=&quot;https://groups.google.com/forum/#!topic/elasticsearch/rPS1duxMzyM&quot;&gt;pulled from the app store by its creator&lt;/a&gt;. There are quite strong opinions in this thread and I would like to have Sense as a Chrome plugin as well. But I am also totally fine with Elasticsearch as a company trying to monetize some of its products so that is maybe something we just have to accept. What is interesting is that it isn't even possible to fork the project and keep developing it as there is no explicit license in the repo. I guess there is a lesson buried somewhere in here.&lt;/p&gt; &lt;p&gt;In this post I would like to look at some of the alternatives for interacting with Elasticsearch. Though the good thing about Sense is that it is independent from the Elasticsearch installation we are looking at plugins here. It might be possible to use some of them without installing them in Elasticsearch but I didn't really try. The plugins are generally doing more things but I am looking at the REST capabilities only.&lt;/p&gt; &lt;h4&gt;Marvel&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.elasticsearch.org/guide/en/marvel/current/&quot;&gt;Marvel&lt;/a&gt; is the commercial plugin by Elasticsearch (free for development purposes). Though it does lots of additional things, it contains the new version of Sense. Marvel will track lots of the state and interaction with Elasticsearch in a seperate index so be aware that it might store quite some data. Also of course you need to respect the license; when using it on a production system you need to pay.&lt;/p&gt; &lt;p&gt;The main Marvel dashboard, which is Kibana, is available at &lt;code&gt;http://localhost:9200/_plugin/marvel&lt;/code&gt;. Sense can be accessed directly using &lt;code&gt;http://localhost:9200/_plugin/marvel/sense/index.html&lt;/code&gt;.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-1pY6ybn5LI8/U60awQ6ngDI/AAAAAAAAAU8/pnHrBri00i4/s1600/marvel-sense.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-1pY6ybn5LI8/U60awQ6ngDI/AAAAAAAAAU8/pnHrBri00i4/s640/marvel-sense.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The Sense version of Marvel behaves exactly like the one you are used from the Chrome plugin. It has highlighting, autocompletion (even for new features), the history and the formatting.&lt;/p&gt; &lt;h4&gt;elasticsearch-head&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://mobz.github.io/elasticsearch-head/&quot;&gt;elasticsearch-head&lt;/a&gt; seems to be one of the oldest plugins available for Elasticsearch and it is recommended a lot. The main dashboard is available at &lt;code&gt;http://localhost:9200/_plugin/head/&lt;/code&gt; which contains the cluster overview.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-dsd8H_skgR8/U60bfpNYvQI/AAAAAAAAAVA/vuw-dBG9fJc/s1600/es-head-overview.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-dsd8H_skgR8/U60bfpNYvQI/AAAAAAAAAVA/vuw-dBG9fJc/s640/es-head-overview.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;There is an interface for building queries at the Structured Query tab./p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-oVAv24UJaU8/U60blATPUoI/AAAAAAAAAVI/D9SyUxRBO54/s1600/es-head-structured.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-oVAv24UJaU8/U60blATPUoI/AAAAAAAAAVI/D9SyUxRBO54/s640/es-head-structured.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;It lets you execute queries by selecting values from dropdown boxes and it can even detect fields that are available for the index and type. Results are displayed in a table. Unfortunately the values that can be selected are rather outdated. Instead of the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/query-dsl-match-query.html&quot;&gt;match query&lt;/a&gt; it still contains the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/query-dsl-text-query.html&quot;&gt;text query&lt;/a&gt; that is deprecated since Elasticsearch 0.19.9 and is not available anymore with newer versions of Elasticsearch.&lt;/p&gt; &lt;p&gt;Another interface on the Any Request tab lets you execute custom requests.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-wuJQIFDdZ9Q/U60bppQt5XI/AAAAAAAAAVQ/87HncWoJTRo/s1600/es-head-any-request.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-wuJQIFDdZ9Q/U60bppQt5XI/AAAAAAAAAVQ/87HncWoJTRo/s640/es-head-any-request.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The text box that accepts the body has no highlighting and it is not possble to use tabs but errors will be displayed, the response is formatted, links are set and you do have the option to use a table or the JSON format for responses. The history lets you execute older queries.&lt;/p&gt; &lt;p&gt;There are other options like Result Transformer that sound interesting but I have never tried those.&lt;/p&gt; &lt;h4&gt;elasticsearch-kopf&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/lmenezes/elasticsearch-kopf&quot;&gt;elasticsearch-kopf&lt;/a&gt; is a clone of elasticsearch-head that also provides an interface to send arbitrary requests to Elasticsearch.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-vvMXWawNF2M/U60bwnO0IGI/AAAAAAAAAVY/aQwh4ABkGQE/s1600/rest.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-vvMXWawNF2M/U60bwnO0IGI/AAAAAAAAAVY/aQwh4ABkGQE/s640/rest.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;You can enter queries and let them be executed for you. There is a request history, you have highlighting and you can format the request document but unfortunately the interface is missing a autocompletion.&lt;/p&gt; &lt;p&gt;If you'd like to learn more about elasticsearch-kopf I have recently published &lt;a href=&quot;http://blog.florian-hopf.de/2014/06/elasticsearch-kopf.html&quot;&gt;a tour through its features&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Inquisitor&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/polyfractal/elasticsearch-inquisitor&quot;&gt;Inquisitor&lt;/a&gt; is a tool to help you understand Elasticsearch queries. Besides other options it allows you to execute search queries.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-LvObdXwYtDA/U60b1ftG9rI/AAAAAAAAAVg/h0YEpqGhQVg/s1600/inquisitor.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-LvObdXwYtDA/U60b1ftG9rI/AAAAAAAAAVg/h0YEpqGhQVg/s640/inquisitor.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Index and type can be chosen from the ones available in the cluster. There is no formatting in the query field, you can't even use tabs for indentation, but errors in your query are displayed in the panel on top of the results while typing. The response is displayed in a table, matching fields are automatically highlighted. Because of the limited possibilites when entering text the plugin seems to be more useful when it comes to the analyzing part or for pasting existing queries&lt;/p&gt; &lt;h4&gt;Elastic-Hammer&lt;/h4&gt; &lt;p&gt;Andrew Cholakian, the author of &lt;a href=&quot;http://exploringelasticsearch.com/&quot;&gt;Exploring Elasticsearch&lt;/a&gt;, has published another query tool, &lt;a href=&quot;https://github.com/andrewvc/elastic-hammer&quot;&gt;Elastic-Hammer&lt;/a&gt;. It can either be installed locally or used as an &lt;a href=&quot;http://elastichammer.exploringelasticsearch.com/&quot;&gt;online version&lt;/a&gt; directly.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-2d30K-dxrjE/U60b6KDOHvI/AAAAAAAAAVo/eVoJIU_Rh64/s1600/elastic-hammer.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-2d30K-dxrjE/U60b6KDOHvI/AAAAAAAAAVo/eVoJIU_Rh64/s640/elastic-hammer.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;It is a quite useful query tool that will display syntactic errors in your query and format images and links in a pretty response. It even offers autocompletion though not as elaborated as the one Sense and Marvel are providing: It will display any allowed term, no matter the context. So you can't really see which terms currently are allowed but only that the term is allowed at all. Nevertheless this can be useful. Searches can also be saved in local storage and executed again.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Currently none of the free and open source plugins seems to provide an interface that is as good as the one contained in Sense and Marvel. As Marvel is free for development you can still use but you need to install it in the instances again. Sense was more convenient and easier to start but I guess one can get along with Marvel the same way. &lt;/p&gt; &lt;p&gt;Finally I wouldn't be surprised if someone from the very active Elasticsearch community comes up with another tool that can take the place of Sense again.&lt;/p&gt;  </content>
 </entry>
 
 <entry>
   <title>An Alternative to the Twitter River - Index Tweets in Elasticsearch with Logstash</title>
   <link href="http://blog.florian-hopf.de/2014/06/an-alternative-to-twitter-river-index.html"/>
   <updated>2014-06-20T15:46:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/06/an-alternative-to-twitter-river-index</id>
   <content type="html">&lt;p&gt;For some time now I've been using the &lt;a href=&quot;https://github.com/elasticsearch/elasticsearch-river-twitter/&quot;&gt;Elasticsearch Twitter river&lt;/a&gt; for &lt;a href=&quot;http://blog.florian-hopf.de/2013/09/simple-event-analytics-with.html&quot;&gt;streaming conference tweets to Elasticsearch&lt;/a&gt;. The river runs on an Elasticsearch node, tracks the Twitter streaming API for keywords and directly indexes the documents in Elasticsearch. As the rivers are about to be deprecated it is time to move on to the recommended replacement: &lt;a href=&quot;http://logstash.net/&quot;&gt;Logstash&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;With Logstash the retrieval of the Twitter data is executed in a different process, probably even on a different machine. This helps in scaling Logstash and Elasticsearch seperately.&lt;/p&gt; &lt;h4&gt;Installation&lt;/h4&gt; &lt;p&gt;The installation of Logstash is nearly as easy as the one for Elasticsearch though you can't start it without a configuration that tells it what you want it to do. You can &lt;a href=&quot;http://www.elasticsearch.org/overview/elkdownloads/&quot;&gt;download it&lt;/a&gt;, unpack the archive and there are scripts to start it. If you are fine with using the embedded Elasticsearch instance you don't even need to install this separately. But you need to have a configuration file in place that tells Logstash what to do exactly.&lt;/p&gt; &lt;h4&gt;Configuration&lt;/h4&gt; &lt;p&gt;The &lt;a href=&quot;http://logstash.net/docs/1.4.1/configuration&quot;&gt;configuration for Logstash&lt;/a&gt; normally consists of three sections: The input, optional filters and the output section. There is a &lt;a href=&quot;http://logstash.net/docs/1.4.1/&quot;&gt;multitude of existing components for each of those available&lt;/a&gt;. The structure of a config file looks like this (taken from the documentation):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;# This is a comment. You should use comments to describe&lt;br /&gt;# parts of your configuration.&lt;br /&gt;input {&lt;br /&gt;  ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;filter {&lt;br /&gt;  ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;output {&lt;br /&gt;  ...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We are using the &lt;a href=&quot;http://logstash.net/docs/1.4.1/inputs/twitter&quot;&gt;Twitter input&lt;/a&gt;, the &lt;a href=&quot;http://logstash.net/docs/1.4.1/outputs/elasticsearch_http&quot;&gt;elasticsearch_http output&lt;/a&gt; and no filters.&lt;/p&gt; &lt;h4&gt;Twitter&lt;/h4&gt; &lt;p&gt;As with any Twitter API interaction you need to have an account and configure the access tokens.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;input {&lt;br /&gt;    twitter {&lt;br /&gt;        # add your data&lt;br /&gt;        consumer_key =&gt; &quot;&quot;&lt;br /&gt;        consumer_secret =&gt; &quot;&quot;&lt;br /&gt;        oauth_token =&gt; &quot;&quot;&lt;br /&gt;        oauth_token_secret =&gt; &quot;&quot;&lt;br /&gt;        keywords =&gt; [&quot;elasticsearch&quot;]&lt;br /&gt;        full_tweet =&gt; true&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You need to pass in all the credentials as well as the &lt;code&gt;keywords&lt;/code&gt; to track. By enabling the &lt;code&gt;full_tweet&lt;/code&gt; option you can index a lot more data, by default there are only a few fields and interesting information like hashtags or mentions are missing.&lt;/p&gt; &lt;p&gt;The Twitter river seems to have different names than the ones that are sent with the raw tweets so it doesn't seem to be possible to easily index Twitter logstash data along with data created by the Twitter river. But it should be no big deal to change the Logstash field names as well with a filter.&lt;/p&gt; &lt;h4&gt;Elasticsearch&lt;/h4&gt; &lt;p&gt;There are three plugins that are providing an output to Elasticsearch: &lt;a href=&quot;http://logstash.net/docs/1.4.1/outputs/elasticsearch&quot;&gt;elasticsearch&lt;/a&gt;, &lt;a href=&quot;http://logstash.net/docs/1.4.1/outputs/elasticsearch_http&quot;&gt;elasticsearch_http&lt;/a&gt; and &lt;a href=&quot;http://logstash.net/docs/1.4.1/outputs/elasticsearch_river&quot;&gt;elasticsearch_river&lt;/a&gt;. elasticsearch provides the opportunity to bind to an Elasticsearch cluster as a node or via transport, elasticsearch_http uses the HTTP API and elasticsearch_river communicates via the RabbitMQ river. The http version lets you use different Elasticsearch versions for Logstash and Elasticsearch, this is the one I am using. Note that the elasticsearch plugin also provides an option for setting the protocol to http that also seems to work.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;output {&lt;br /&gt;    elasticsearch_http {&lt;br /&gt;        host =&gt; &quot;localhost&quot;&lt;br /&gt;        index =&gt; &quot;conf&quot;&lt;br /&gt;        index_type =&gt; &quot;tweet&quot;&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;In contrast to the Twitter river the Logstash plugin does not create a special mapping for the tweets. I didn't go through all the fields but for example the coordinates don't seem to be mapped correctly to &lt;code&gt;geo_point&lt;/code&gt; and some fields are analyzed that probably shouldn't be (urls, usernames). If you are using those you might want to prepare your index by supplying it with a custom mapping.&lt;/p&gt; &lt;p&gt;By default tweets will be pushed to Elasticsearch every second which should be enough for any analysis. You can even think about reducing this with the property &lt;code&gt;idle_flush_time&lt;/code&gt;.&lt;/p&gt; &lt;h4&gt;Running&lt;/h4&gt; &lt;p&gt;Finally, when all of the configuration is in place you can execute Logstash using the following command (assuming the configuration is in a file twitter.conf):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;bin/logstash agent -f twitter.conf&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Nothing left to do but wait for the first tweets to arrive in your local instance at http://localhost:9200/conf/tweet/_search?q=*:*&amp;pretty=true.&lt;/p&gt; &lt;p&gt;For the future it would be really useful to prepare a mapping for the fields and a filter that removes some of the unused data. For now you have to check what you would like to use of the data and prepare a mapping in advance.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>A Tour Through elasticsearch-kopf</title>
   <link href="http://blog.florian-hopf.de/2014/06/elasticsearch-kopf.html"/>
   <updated>2014-06-13T13:49:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/06/elasticsearch-kopf</id>
   <content type="html">&lt;p&gt;When I needed a plugin to display the cluster state of Elasticsearch or needed some insight into the indices I normally reached for the classic plugin &lt;a href=&quot;http://mobz.github.io/elasticsearch-head/&quot;&gt;elasticsearch-head&lt;/a&gt;. As it is recommended a lot and seems to be the unofficial successor I recently took a more detailed look at &lt;a href=&quot;https://github.com/lmenezes/elasticsearch-kopf&quot;&gt;elasticsearch-kopf&lt;/a&gt;. And I like it.&lt;/p&gt; &lt;p&gt;I am not sure about why elasticsearch-kopf came into existence but it seems to be a clone of elasticsearch-head (kopf means head in German so it is even the same name).&lt;/p&gt; &lt;h4&gt;Installation&lt;/h4&gt; &lt;p&gt;elasticsearch-kopf can be installed like most of the plugins, using the script in the Elasticsearch installation. This is the command that installs the version 1.1 which is suitable for the 1.1.x branch of Elasticsearch.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;bin/plugin --install lmenezes/elasticsearch-kopf/1.1&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;elasticsearch-kopf is then available on the url http://localhost:9200/_plugin/kopf/.&lt;/p&gt; &lt;h4&gt;Cluster&lt;/h4&gt; &lt;p&gt;On the front page you will see a similar diagram of what elasticsearch-head is providing. The overview of your cluster with all the shards and the distribution across the nodes. The page is being refreshed so you will see joining or leaving nodes immediately. You can adjust the refresh rate in the settings dropdown just next to the kopf logo (by the way, the header reflects the state of the cluster so it might change its color from green to yellow to red).&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-3Mndb2do-mE/U5qLoTRpeaI/AAAAAAAAATU/k0iqK3196Dg/s1600/overview.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-3Mndb2do-mE/U5qLoTRpeaI/AAAAAAAAATU/k0iqK3196Dg/s640/overview.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Also, there are lots of different settings that can be reached via this page. On top of the node list there are 4 icons for creating a new index, deactivating shard allocation, for the cluster settings and the cluster diagnosis options.&lt;/p&gt; &lt;p&gt;Creating a new index brings up a form for entering the index data. You can also load the settings from an existing index or just paste the settings json in the field on the right side.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-VCHHDwnH258/U5qL5gVo1wI/AAAAAAAAATc/s7bBnh5wi_8/s1600/create-index.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-VCHHDwnH258/U5qL5gVo1wI/AAAAAAAAATc/s7bBnh5wi_8/s640/create-index.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The icon for disabling the shard allocation just toggles it, disabling the shard allocation can be useful during a cluster restart. Using the cluster settings you can reach a form where you can adjust lots of values regarding your cluster, the routing and recovery. The cluster health button finally lets you load different json documents containing more details on the cluster health, e.g. the nodes stats and the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster-nodes-hot-threads.html&quot;&gt;hot threads&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Using the little dropdown just next to the index name you can execute some operations on the index. You can view the settings, &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html&quot;&gt;open and close the index&lt;/a&gt;, optimize and refresh the index, &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-clearcache.html&quot;&gt;clear the caches&lt;/a&gt;, adjust the settings or delete the index.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-KFozXI9Gv3w/U5qNVyT6GuI/AAAAAAAAATw/DWdbCHfDkv8/s1600/index-menu.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-KFozXI9Gv3w/U5qNVyT6GuI/AAAAAAAAATw/DWdbCHfDkv8/s640/index-menu.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;When opening the form for the index settings you will be overwhelmed at first. I didn't know there are so many settings. What is really useful is that there is an info icon next to each field that will tell you what this field is about. A great opportunity to learn about some of the settings.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-En7RNPV2-OA/U5qNPlxukaI/AAAAAAAAATo/ATiqYIuPr04/s1600/index-settings.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-En7RNPV2-OA/U5qNPlxukaI/AAAAAAAAATo/ATiqYIuPr04/s640/index-settings.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;What I find really useful is that you can adjust the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules-slowlog.html&quot;&gt;slow index log&lt;/a&gt; settings directly. The slow log can also be used to log any incoming queries so it is sometimes useful for diagnostic purposes.&lt;/p&gt; &lt;p&gt;Finally, back on the cluster page, you can get more detailed information on the nodes or shards when clicking on them. This will open a lightbox with more details.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-rsyqDTEzSuc/U5qNlj1kNTI/AAAAAAAAAT4/E5KWJjE5wbs/s1600/shard-info.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-rsyqDTEzSuc/U5qNlj1kNTI/AAAAAAAAAT4/E5KWJjE5wbs/s640/shard-info.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;REST&lt;/h4&gt; &lt;p&gt;The rest menu entry on top brings you to another view which is similar to the one &lt;a href=&quot;https://www.found.no/foundation/Sense-Elasticsearch-interface/&quot;&gt;Sense&lt;/a&gt; provided. You can enter queries and let them be executed for you. There is a request history, you have highlighting and you can format the request document but unfortunately the interface is missing the autocompletion. Nevertheless I suppose this can be useful if you don't like to fiddle with curl.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-Z4r-ZQ-vmCI/U5qNqwIz8NI/AAAAAAAAAUA/lgxaLBeAInI/s1600/rest.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-Z4r-ZQ-vmCI/U5qNqwIz8NI/AAAAAAAAAUA/lgxaLBeAInI/s640/rest.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Aliases&lt;/h4&gt; &lt;p&gt;Using the aliases tab you can have a convenient form for managing your &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-aliases.html&quot;&gt;index aliases&lt;/a&gt; and all the relevant additional information. You can add filter queries for your alias or influence the index or search routing. On the right side you can see the existing aliases and remove them if not needed.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-og-_QSoix2A/U5qOBCP6rUI/AAAAAAAAAUI/GyEiL83ZZLs/s1600/alias.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-og-_QSoix2A/U5qOBCP6rUI/AAAAAAAAAUI/GyEiL83ZZLs/s640/alias.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Analysis&lt;/h4&gt; &lt;p&gt;The analysis tab will bring you to a feature that is also very popular for the Solr administration view. You can test the analyzers for different values and different fields. This is a very valuable tool while building a more complex search application.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-NCNNuZvdhjo/U5qOFHsGgnI/AAAAAAAAAUQ/RSU2uUx8KH4/s1600/analysis.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-NCNNuZvdhjo/U5qOFHsGgnI/AAAAAAAAAUQ/RSU2uUx8KH4/s640/analysis.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Unfortunately the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-analyze.html&quot;&gt;information you can get from Elasticsearch&lt;/a&gt; is not as detailed as the one you can get from Solr: It will only contain the end result so you can't really see which tokenizer or filter caused a certain change.&lt;/p&gt; &lt;h4&gt;Percolator&lt;/h4&gt; &lt;p&gt;On the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-percolate.html&quot;&gt;percolator&lt;/a&gt; tab you can use a form to register new percolator queries and view existing ones. There doesn't seem to be a way to do the actual percolation but maybe this page can be useful for using the percolator extensively.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-Ql1pxyWWMio/U5qOKKDSdcI/AAAAAAAAAUY/XZ7dfc4hbsc/s1600/percolator.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-Ql1pxyWWMio/U5qOKKDSdcI/AAAAAAAAAUY/XZ7dfc4hbsc/s640/percolator.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Warmers&lt;/h4&gt; &lt;p&gt;The warmers tab can be used to register &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-warmers.html&quot;&gt;index warmer queries&lt;/a&gt;. &lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-QB56X-zEFTA/U5qOOFpjuHI/AAAAAAAAAUg/P6N3XbLomMg/s1600/warmers.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-QB56X-zEFTA/U5qOOFpjuHI/AAAAAAAAAUg/P6N3XbLomMg/s640/warmers.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Repository&lt;/h4&gt; &lt;p&gt;The final tab is for the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-snapshots.html&quot;&gt;snapshot and restore&lt;/a&gt; feature. You can create repositories and snapshots and restore them. Though I can imagine that most of the people are automating the snapshot creation this can be a very useful form. &lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-B6m6nyGyca4/U5qOS-91fDI/AAAAAAAAAUo/Nc8cwoEyM84/s1600/snapshots.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-B6m6nyGyca4/U5qOS-91fDI/AAAAAAAAAUo/Nc8cwoEyM84/s640/snapshots.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;I hope you could see in this post that elasticsearch-kopf can be really useful. It is very unlikely that you will ever need all of the forms but it is good to have them available. The cluster view and the rest interface can be very valuable for your daily work and I guess there will be new features coming in the future.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>See Your Solr Cache Sizes: Eclipse Memory Analyzer </title>
   <link href="http://blog.florian-hopf.de/2014/05/solr-cache-sizes-eclipse-memory-analyzer.html"/>
   <updated>2014-05-09T15:44:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/05/solr-cache-sizes-eclipse-memory-analyzer</id>
   <content type="html">&lt;p&gt;Solr uses different &lt;a href=&quot;http://wiki.apache.org/solr/SolrCaching&quot;&gt;caches&lt;/a&gt; to prevent too much IO access and calculations during requests. When indexing doesn't happen too frequently you can get huge performance gains by employing those caches. Depending on the structure of your index data and the size of the caches they can become rather large and use a substantial part of your heap memory. In this post I would like to show how you can use the &lt;a href=&quot;http://www.eclipse.org/mat/&quot;&gt;Eclipse Memory Analyzer&lt;/a&gt; to see how much space your caches are really using in memory.&lt;/p&gt; &lt;h4&gt;Configuring the Caches&lt;/h4&gt; &lt;p&gt;All the Solr caches can be configured in solrconfig.xml in the &lt;code&gt;query&lt;/code&gt; section. You will find definitions like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;filterCache class=&amp;quot;solr.FastLRUCache&amp;quot;&lt;br /&gt;  size=&amp;quot;8000&amp;quot;&lt;br /&gt;  initialSize=&amp;quot;512&amp;quot;&lt;br /&gt;  autowarmCount=&amp;quot;0&amp;quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This is an example of a filter cache configured to use the &lt;code&gt;FastLRUCache&lt;/code&gt;, a maximum size of 8000 items and no autowarming. Solr ships with two commonly used cache implementations, the &lt;code&gt;FastLRUCache&lt;/code&gt;, that uses a &lt;code&gt;ConcurrentHashMap&lt;/code&gt; and the &lt;code&gt;LRUCache&lt;/code&gt;, that synchronizes the calls. Some of the caches are still configured to use the &lt;code&gt;LRUCache&lt;/code&gt; but on some read heavy projects I had good results with changing those to &lt;code&gt;FastLRUCache&lt;/code&gt; as well.&lt;/p&gt; &lt;p&gt;Additionaly, starting from Solr 3.6 there is also the &lt;a href=&quot;http://lucene.apache.org/solr/4_8_0/solr-core/org/apache/solr/search/LFUCache.html&quot;&gt;LFUCache&lt;/a&gt;. I have never used it and it is still marked as experimental and subject to change.&lt;/p&gt; &lt;p&gt;Solr comes with the following caches:&lt;/p&gt; &lt;dl&gt;&lt;dt&gt;FilterCache&lt;/dt&gt;&lt;dd&gt;Caches a bitset of the filter queries. This can be a very effective cache if you are reusing filters.&lt;/dd&gt;&lt;dt&gt;QueryResultCache&lt;/dt&gt;&lt;dd&gt;Stores an ordered list of the document ids for a query.&lt;/dd&gt;&lt;dt&gt;DocumentCache&lt;/dt&gt;&lt;dd&gt;Caches the stored fields of the Lucene documents. If you have large or many fields this cache can become rather large.&lt;/dd&gt;&lt;dt&gt;FieldValueCache&lt;/dt&gt;&lt;dd&gt;A cache that is mainly used for faceting.&lt;/dd&gt;&lt;/dl&gt; &lt;p&gt;Additionaly you will see references to the FieldCache which is not a cache managed by Solr and can not be configured.&lt;/p&gt; &lt;p&gt;In the default configuration Solr only caches 512 items per cache which can often be too small. You can see the usage of your cache in the administration view of Solr in the section Plugin/Stats/Caches of your core. This will tell you the hit rate as well as the evictions for your caches.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-m15MTpIHyN4/U2yBMzFfwPI/AAAAAAAAASY/P-7A3iohncg/s1600/admin.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-m15MTpIHyN4/U2yBMzFfwPI/AAAAAAAAASY/P-7A3iohncg/s640/admin.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The stats are a good starting point for tuning your caches but you should be aware that by setting the size too large you can see some unwanted GC activity. That is why it might be useful to look at the real size of your caches in memory instead of the item count alone.&lt;/p&gt; &lt;h4&gt;Eclipse MAT&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.eclipse.org/mat/&quot;&gt;Eclipse MAT&lt;/a&gt; is a great tool for looking at your heap in memory and see which objects occupy the space. As the name implies it is based on Eclipse and can either be downloaded as a standalone tool or is available via update sites for integration in an existing instance.&lt;/p&gt; &lt;p&gt;Heap dumps can be aquired using the tool directly but you can also open existing dumps. On opening it will automatically calculate a chart of the largest objects that might already contain some of the cache objects, if you are keeping lots of items in the cache.&lt;/p&gt;   &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-qgok7O_GzM0/U2yBad5TkjI/AAAAAAAAASg/k-XFxe5ySn8/s1600/biggest-objects.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-qgok7O_GzM0/U2yBad5TkjI/AAAAAAAAASg/k-XFxe5ySn8/s640/biggest-objects.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Using the links below the pie chart you can also open further automatic reports, e.g. the Top Consumers, a more detailed page on large objects.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-2mjG3RfsGmY/U2yBfOFEL2I/AAAAAAAAASo/_w8LjAInJ2A/s1600/top-consumers.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-2mjG3RfsGmY/U2yBfOFEL2I/AAAAAAAAASo/_w8LjAInJ2A/s640/top-consumers.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Even if you do see some of the cache classes here, you can't really see which of the caches it is that consumes the memory. Using the Query Browser menu on top of the report you can also list instances of classes directly, no matter how large those are.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-mVEw4y5fB1w/U2yB1fkQ1hI/AAAAAAAAAS4/qSnl2H9NjbE/s1600/menu.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-mVEw4y5fB1w/U2yB1fkQ1hI/AAAAAAAAAS4/qSnl2H9NjbE/s640/menu.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;We are choosing List Objects with outgoing references and enter the class name for the FastLRUCache, &lt;code&gt;org.apache.solr.search.FastLRUCache&lt;/code&gt;. For the default configuration you will see two instances. When clicking on one of the instances you can see the name of the cache in the lower left window, in this case the filter cache.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-cPCSzrCqvTc/U2yBlOQf1vI/AAAAAAAAASw/zYMGgXur66k/s1600/FastLRUCaches.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-cPCSzrCqvTc/U2yBlOQf1vI/AAAAAAAAASw/zYMGgXur66k/s640/FastLRUCaches.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;There are two numbers available for the heap size: The &lt;a href=&quot;http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fshallowretainedheap.html&quot;&gt;shallow size and the retained size&lt;/a&gt;. When looking at the caches we are interested in the retained size as this is the size that would be available when the instance is garbage collected, i.e. the size of the cache that is only used by the cache. In our case this is around 700kB but this can grow a lot.&lt;/p&gt; &lt;p&gt;You can also do the same inspection for the &lt;code&gt;org.apache.solr.search.LRUCache&lt;/code&gt; to see the real size of your caches.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;The caches can get a lot bigger than in our example here. Eclipse Memory Analyzer has helped me a lot already to see if there are any problems with a heap that is growing too large.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>What Is Special About This? Significant Terms in Elasticseach</title>
   <link href="http://blog.florian-hopf.de/2014/04/significant-terms-elasticsearch.html"/>
   <updated>2014-04-30T10:32:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/04/significant-terms-elasticsearch</id>
   <content type="html">&lt;p&gt;I have been using Elasticsearch a few times now for doing &lt;a href=&quot;http://blog.florian-hopf.de/2013/09/kibana-and-elasticsearch-see-what.html&quot;&gt;analytics of twitter data for conferences&lt;/a&gt;. Popular hashtags and mentions that can be extraced using facets can show what is hot at a conference. But you can go even further and see what makes each hashtag special. In this post I would like to show you the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html&quot;&gt;significant terms aggregation&lt;/a&gt; that is available with Elasticsearch 1.1. I am using the &lt;a href=&quot;http://blog.florian-hopf.de/2013/11/devoxx-in-tweets.html&quot;&gt;tweets of last years Devoxx&lt;/a&gt; as those contain enough documents to play around.&lt;/p&gt; &lt;h4&gt;Aggregations&lt;/h4&gt; &lt;p&gt;Elasticsearch 1.0 introduced &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html&quot;&gt;aggregations&lt;/a&gt;, that can be used similar to facets but are far more powerful. To see why those are useful let's take a step back and look at facets, that are often used to extract statistical values and distributions. One useful example for facets is the total count of a hashtag:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/devoxx/tweet/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;size&quot;: 0,  &lt;br /&gt;    &quot;facets&quot;: {&lt;br /&gt;       &quot;hashtags&quot;: {&lt;br /&gt;          &quot;terms&quot;: {&lt;br /&gt;             &quot;field&quot;: &quot;hashtag.text&quot;,&lt;br /&gt;             &quot;size&quot;: 10,&lt;br /&gt;             &quot;exclude&quot;: [&lt;br /&gt;                &quot;devoxx&quot;, &quot;dv13&quot;&lt;br /&gt;             ]&lt;br /&gt;          }&lt;br /&gt;       }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We request a facet called hashtags that uses the terms of hashtag.text and returns the 10 top values with the counts. We are excluding the hashtags devoxx and dv13 as those are very frequent. This is an excerpt of the result with the popular hashtags:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;   &quot;facets&quot;: {&lt;br /&gt;      &quot;hashtags&quot;: {&lt;br /&gt;         &quot;_type&quot;: &quot;terms&quot;,&lt;br /&gt;         &quot;missing&quot;: 0,&lt;br /&gt;         &quot;total&quot;: 19219,&lt;br /&gt;         &quot;other&quot;: 17908,&lt;br /&gt;         &quot;terms&quot;: [&lt;br /&gt;            {&lt;br /&gt;               &quot;term&quot;: &quot;dartlang&quot;,&lt;br /&gt;               &quot;count&quot;: 229&lt;br /&gt;            },&lt;br /&gt;            {&lt;br /&gt;               &quot;term&quot;: &quot;java&quot;,&lt;br /&gt;               &quot;count&quot;: 216&lt;br /&gt;            },&lt;br /&gt;            {&lt;br /&gt;               &quot;term&quot;: &quot;android&quot;,&lt;br /&gt;               &quot;count&quot;: 139&lt;br /&gt;            },&lt;br /&gt;    [...]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Besides the statistical information we are retrieving here facets are often used for offering a refinement on search results. A common use is to display categories or features of products on eCommerce sites for example.&lt;/p&gt; &lt;p&gt;Starting with Elasticsearch 1.0 you can have the same behaviour by using one of the new aggregations, in this case a &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html&quot;&gt;terms aggregation&lt;/a&gt;:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/devoxx/tweet/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;size&quot; : 0,&lt;br /&gt;    &quot;aggs&quot; : {&lt;br /&gt;        &quot;hashtags&quot; : {&lt;br /&gt;            &quot;terms&quot; : { &lt;br /&gt;                &quot;field&quot; : &quot;hashtag.text&quot;, &lt;br /&gt;                &quot;exclude&quot; : &quot;devoxx|dv13&quot;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Instead of requesting facets we are now requesting a terms aggregation for the field hashtag.text. The exclusion is now based on a regular expression instead of a list. The result looks similar to the facet return values:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;   &quot;aggregations&quot;: {&lt;br /&gt;      &quot;hashtags&quot;: {&lt;br /&gt;         &quot;buckets&quot;: [&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;dartlang&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 229&lt;br /&gt;            },&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;java&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 216&lt;br /&gt;            },&lt;br /&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;android&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 139&lt;br /&gt;            },&lt;br /&gt;    [...]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Each value forms a so called bucket that contains a key and a doc_count.&lt;/p&gt; &lt;p&gt;But aggregations not only are a replacement for facets. Multiple aggregations can be combined to give more information on the distribution of different fields. For example we can see the users that used a certain hashtag by adding a second terms aggregation for the field user.screen_name:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/devoxx/tweet/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;size&quot; : 0,&lt;br /&gt;    &quot;aggs&quot; : {&lt;br /&gt;        &quot;hashtags&quot; : {&lt;br /&gt;            &quot;terms&quot; : { &lt;br /&gt;                &quot;field&quot; : &quot;hashtag.text&quot;, &lt;br /&gt;                &quot;exclude&quot; : &quot;devoxx|dv13&quot;&lt;br /&gt;            },&lt;br /&gt;            &quot;aggs&quot; : {&lt;br /&gt;                &quot;hashtagusers&quot; : {&lt;br /&gt;                    &quot;terms&quot; : {&lt;br /&gt;                        &quot;field&quot; : &quot;user.screen_name&quot;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Using this nested aggregation we now get a list of buckets for each hashtag. This list contains the users that used the hashtag. This is a short excerpt for the #scala hashtag:&lt;/p&gt; &lt;pre&gt;&lt;code&gt; &lt;br /&gt;               &quot;key&quot;: &quot;scala&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 130,&lt;br /&gt;               &quot;hashtagusers&quot;: {&lt;br /&gt;                  &quot;buckets&quot;: [&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;jaceklaskowski&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 74&lt;br /&gt;                     },&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;ManningBooks&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 3&lt;br /&gt;                     },&lt;br /&gt;    [...]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We can see that there is one user that is responsible for half of the hashtags. A very dedicated user.&lt;/p&gt; &lt;p&gt;Using aggregations we can get information that we were not able to get with facets alone. If you are interested in more details about aggregations in general or the metrics aggregations I haven't touched here, &lt;a href=&quot;http://chrissimpson.co.uk/elasticsearch-aggregations-overview.html&quot;&gt;Chris Simpson has written a nice post on the feature&lt;/a&gt;, there is &lt;a href=&quot;https://www.found.no/foundation/elasticsearch-aggregations/&quot;&gt;a nice visual one at the Found blog&lt;/a&gt;, &lt;a href=&quot;http://blog.qbox.io/elasticsearch-aggregations&quot;&gt;another one here&lt;/a&gt; and of course there is &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html&quot;&gt;the official documentation on the Elasticsearch website&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Significant Terms&lt;/h4&gt; &lt;p&gt;Elasticsearch 1.1 contains a new aggregation, the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html&quot;&gt;significant terms aggregation&lt;/a&gt;. It allows you to do something very useful: For each bucket that is created you can see the terms that make this bucket special.&lt;/p&gt; &lt;p&gt;Significant terms are calculated by comparing a foreground frequency (which is the frequency of the bucket you are interested in) with a background frequency (which for Elasticsearch 1.1 always is the frequency of the complete index). This means it will collect any results that have a high frequency for the current bucket but not for the complete index.&lt;/p&gt; &lt;p&gt;For our example we can now check for the hashtags that are often used with a certain mention. This is not the same that can be done with the terms aggregation. The significant terms will only return those terms that are occuring often for a certain user but not as frequently for all users. This is what &lt;a href=&quot;http://www.infoq.com/presentations/elasticsearch-revealing-uncommonly-common&quot;&gt;Mark Harwood calls the &lt;q&gt;uncommonly common&lt;/q&gt;&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/devoxx/tweet/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;size&quot; : 0,&lt;br /&gt;    &quot;aggs&quot; : {&lt;br /&gt;        &quot;mentions&quot; : {&lt;br /&gt;            &quot;terms&quot; : { &lt;br /&gt;                &quot;field&quot; : &quot;mention.screen_name&quot; &lt;br /&gt;            },&lt;br /&gt;            &quot;aggs&quot; : {&lt;br /&gt;                &quot;uncommonhashtags&quot; : {&lt;br /&gt;                    &quot;significant_terms&quot; : {&lt;br /&gt;                        &quot;field&quot; : &quot;hashtag.text&quot;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We request a normal terms aggregation for the mentioned users. Using a nested significant_terms aggregation we can see any hashtags that are often used with the mentioned user but not so often in the whole index. This is a snippet for the account of Brian Goetz:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;            {&lt;br /&gt;               &quot;key&quot;: &quot;BrianGoetz&quot;,&lt;br /&gt;               &quot;doc_count&quot;: 173,&lt;br /&gt;               &quot;uncommonhashtags&quot;: {&lt;br /&gt;                  &quot;doc_count&quot;: 173,&lt;br /&gt;                  &quot;buckets&quot;: [&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;lambda&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 13,&lt;br /&gt;                        &quot;score&quot;: 1.8852860861614915,&lt;br /&gt;                        &quot;bg_count&quot;: 33&lt;br /&gt;                     },&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;jdk8&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 8,&lt;br /&gt;                        &quot;score&quot;: 0.7193691737111163,&lt;br /&gt;                        &quot;bg_count&quot;: 32&lt;br /&gt;                     },&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;java&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 21,&lt;br /&gt;                        &quot;score&quot;: 0.6601749139630457,&lt;br /&gt;                        &quot;bg_count&quot;: 216&lt;br /&gt;                     },&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;performance&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 4,&lt;br /&gt;                        &quot;score&quot;: 0.6574225667412876,&lt;br /&gt;                        &quot;bg_count&quot;: 9&lt;br /&gt;                     },&lt;br /&gt;                     {&lt;br /&gt;                        &quot;key&quot;: &quot;keynote&quot;,&lt;br /&gt;                        &quot;doc_count&quot;: 9,&lt;br /&gt;                        &quot;score&quot;: 0.5442707998673785,&lt;br /&gt;                        &quot;bg_count&quot;: 52&lt;br /&gt;                     },&lt;br /&gt;        [...]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can see that there are some tags that are targeted a lot at the keynote by Brian Goetz and are not that common for the whole index.&lt;/p&gt; &lt;p&gt;Some more ideas what we could look at with the significant terms aggregation:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;Find users that are using a hashtag a lot.&lt;/li&gt;&lt;li&gt;Find terms that are often used with a certain hashtag.&lt;/li&gt;&lt;li&gt;Find terms that are used by a certain user.&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Besides these impressive analytics feature significant terms can also be used for search applications. A useful example is given in the Elasticsearch documentation itself: If a user searches for &quot;bird flu&quot; automatically display a link to a search to H5N1 which should be very common in the result documents but not in the whole of the corpus.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;With significant terms Elasticsearch has again added a feature that might very well offer surprising new applications and use cases for search. Not only is it important for analytics but it can also be used to improve classic search applications. Mark Harwood has collected some &lt;a href=&quot;http://www.elasticsearch.org/blog/significant-terms-aggregation&quot;&gt;really interesting use cases on the Elasticsearch blog&lt;/a&gt;. If you'd like to read another post on the topic you can see &lt;a href=&quot;http://blog.qbox.io/elasticsearch-aggregations-1-1-0&quot;&gt;this post at QBox-Blog that introduces significant terms as well as the percentile and cardinality aggregations. &lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>The Absolute Basics of Indexing Data</title>
   <link href="http://blog.florian-hopf.de/2014/04/the-absolute-basics-of-indexing-data.html"/>
   <updated>2014-04-11T14:34:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/04/the-absolute-basics-of-indexing-data</id>
   <content type="html">&lt;p&gt;Ever wondered how a search engine works? In this post I would like to show you a high level view of the internal workings of a search engine and how it can be used to give fast access to your data. I won't go into any technical details, what I am describing here holds true for any &lt;a href=&quot;http://lucene.apache.org&quot;&gt;Lucene&lt;/a&gt; based search engine, be it Lucene itself, &lt;a href=&quot;http://lucene.apache.org/solr&quot;&gt;Solr&lt;/a&gt; or &lt;a href=&quot;http://elasticsearch.org&quot;&gt;Elasticsearch&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Input&lt;/h4&gt; &lt;p&gt;Normally a search engine is agnostic to the real data source of indexing data. Most often you push data into it via an API that already needs to be in the expected format, mostly Strings and data types like integers. It doesn't matter if this data originally resides in a document in the filesystem, on a website or in a database.&lt;/p&gt; &lt;p&gt;Search engines are working with documents that consist of fields and values. Though not always used directly you can think of documents as JSON documents. For this post imagine we are building a book database. In our simplified world a book just consists of a title and one or more authors. This would be two example documents:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;{&lt;br /&gt;    &quot;title&quot; : &quot;Search Patterns&quot;,&lt;br /&gt;    &quot;authors&quot; : [ &quot;Morville&quot;, &quot;Callender&quot; ],&lt;br /&gt;}&lt;br /&gt;{&lt;br /&gt;    &quot;title&quot; : &quot;Apache Solr Enterprise Search Server&quot;,&lt;br /&gt;    &quot;authors&quot; : [ &quot;Smiley&quot;, &quot;Pugh&quot; ]&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Even though the structure of both documents is the same in our case, the format of the document doesn't need to be fixed. Both documents could have totally different attributes, nevertheless both could be stored in the same index. In reality you will try to keep the documents similar, after all, you need a way to handle the documents in your application.&lt;/p&gt; &lt;p&gt;Lucene itself doesn't even have the concept of a key. But of course you need a key to identify your documents when updating them. Both Solr and Elasticsearch have ids that can either be chosen by the application or be autogenerated.&lt;/p&gt; &lt;h4&gt;Analyzing&lt;/h4&gt; &lt;p&gt;For every field that is indexed a special process called analyzing is employed. What it does can differ from field to field. For example, in a simple case it might just split the terms on whitespace and remove any punctuation so &lt;em&gt;Search Patterns&lt;/em&gt; would become two terms, &lt;em&gt;Search&lt;/em&gt; and &lt;em&gt;Patterns&lt;/em&gt;.&lt;/p&gt; &lt;h4&gt;Index Structure&lt;/h4&gt; &lt;p&gt;An inverted index, the structure search engines are using, is similar to a map that contains search terms as key and a reference to a document as value. This way the process of searching is just a lookup of the term in the index, a very fast process. Those might be the terms that are indexed for our example documents.&lt;/p&gt; &lt;table class=&quot;table&quot;&gt;&lt;tr&gt;&lt;th&gt;Field&lt;/th&gt;&lt;th&gt;Term&lt;/th&gt;&lt;th&gt;Document Id&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;title&lt;/td&gt;&lt;td&gt;Apache&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Enterprise&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Patterns&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Search&lt;/td&gt;&lt;td&gt;1,2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Server&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Solr&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;author&lt;/td&gt;&lt;td&gt;Callender&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Morville&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Pugh&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Smiley&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;A real index contains more information like position information to enable phrase queries and frequencies for calculating the relevancy of a document for a certain search term.&lt;/p&gt; &lt;p&gt;As we can see the index holds a reference to the document. This document, that is also stored with the search index, doesn't necessarily have to be the same as our input document. You can determine for each field if you would like to keep the original content which is normally controlled via an attribute named stored. As a general rule, you should have all the fields stored that you would like to display with the search results. When indexing lots of complete books and you don't need to display it in a results page it might be better to not store it at all. You can still search it, as the terms are available in the index, but you can't access the original content.&lt;/p&gt; &lt;h4&gt;More on Analyzing&lt;/h4&gt; &lt;p&gt;Looking at the index structure above we can already imagine how the search process for a book might work. The user enters a term, e.g. &lt;em&gt;Solr&lt;/em&gt;, this term is then used to lookup the documents that contain the term. This works fine for cases when the user types the term correctly. A search for &lt;em&gt;solr&lt;/em&gt; won't match for our current example.&lt;/p&gt; &lt;p&gt;To mitigate those difficulties we can use the analyzing process already mentioned above. Besides the tokenization that splits the field value into tokens we can do further preprocessing like removing tokens, adding tokens or modifying tokens (TokenFilter).&lt;/p&gt; &lt;p&gt;For our book case it might at first be enough to do lowercasing on the incoming data. So a field value &lt;em&gt;Solr&lt;/em&gt; will then be stored as &lt;em&gt;solr&lt;/em&gt; in the index. To enable the user to also search for &lt;em&gt;Solr&lt;/em&gt; with an uppercase letter we need to do analyzing for the query as well. Often it is the same process that is used for indexing but there are also cases for different analyzers.&lt;/p&gt; &lt;p&gt;The analyzing process not only depends on the content of the documents (field types, language of text fields) but also on your application. Take one common scenario: Adding synonyms for terms to the index. You might think that you just take a huge list of synonyms like &lt;a href=&quot;http://wordnet.princeton.edu/&quot;&gt;WordNet&lt;/a&gt; and add those to each of your application. This might in fact decrease the search experience of your users as there are too many false positives. Also, for certain terms of the domain of your users WordNet might not contain the correct synonyms at all.&lt;/p&gt; &lt;h4&gt;Duplication&lt;/h4&gt; &lt;p&gt;When designing the index structure there are two competing forces: Often you either optimize for query speed or for index size. If you have a lot of data you probably need to take care that you only store data that you really need and even only put terms in the index that are necessary for lookups. Oftentimes for smaller datasets the index size doesn't matter that much and you can design your index for query performance.&lt;/p&gt; &lt;p&gt;Let's look at an example that can make sense for both cases. In our book information system we would like to display an alphabetic navigation for the last name of the author. If the user clicks on A, all the books of authors starting with the letter A should be displayed. When using the Lucene query syntax you can do something like this with its wildcard support: Just issue a query that contains the letter the user clicked and a trailing *, e.g. &lt;em&gt;a*&lt;/em&gt;.&lt;/p&gt; &lt;p&gt;Wildcard queries have become very fast with recent Lucene versions, nevertheless it still is a query time impact. You can also choose another way. When indexing the data you can add another field that just stores the first letter of the name. This is what the relevant configuration might look like in Elasticsearch but the concept is the same for Lucene and Solr:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&quot;author&quot;: {&lt;br /&gt;    &quot;type&quot;: &quot;multi_field&quot;,&lt;br /&gt;    &quot;fields&quot;: {&lt;br /&gt;        &quot;author&quot; : {&lt;br /&gt;            &quot;type&quot;: &quot;string&quot;&lt;br /&gt;        },&lt;br /&gt;        &quot;letter&quot; : {&lt;br /&gt;            &quot;type&quot;: &quot;string&quot;,&lt;br /&gt;            &quot;analyzer&quot;: &quot;single_char_analyzer&quot;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Under the hood, another term dictionary for the field author.letter will be created. For our example it will look like this:&lt;/p&gt; &lt;table class=&quot;table&quot;&gt;&lt;tr&gt;&lt;th&gt;Field&lt;/th&gt;&lt;th&gt;Term&lt;/th&gt;&lt;th&gt;Document Id&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;author.letter&lt;/td&gt;&lt;td&gt;C&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;M&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;P&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;S&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;Now, instead of issuing a wildcard query on the author field we can directly query the author.letter field with the letter. You can even build the navigation from all the terms in the index using techniques like faceting the extract all the available terms for a field from the index.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;These are the basics of the indexing data for a search engine. The inverted index structure makes searching really fast by moving some processing to the indexing phase. When we are not bound by any index size concerns we can design our index for query performance and add additional fields that duplicate some of the data. This design for queries is what makes search engines similar to how lots of the NoSQL solutions are used.&lt;/p&gt; &lt;p&gt;If you'd like to go deeper in the topics I recommend watching the talk &lt;a href=&quot;https://www.youtube.com/watch?v=T5RmMNDR5XI&quot;&gt;What is in a Lucene index&lt;/a&gt; by Adrien Grand. He shows some of the concepts I have mentioned here (and a lot more) but also how those are implemented in Lucene.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>BEDCon - Berlin Expert Days 2014</title>
   <link href="http://blog.florian-hopf.de/2014/04/bedcon-berlin-expert-days-2014.html"/>
   <updated>2014-04-07T12:48:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/04/bedcon-berlin-expert-days-2014</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://bed-con.org/&quot;&gt;BEDCon&lt;/a&gt; is over again. This marks the third year I have been there and it still has the cheapest prices for a general conference I have seen in Germany. If you are looking for a nice conference in Berlin you should definitively consider it.&lt;/p&gt; &lt;h4&gt;Interesting Talks&lt;/h4&gt; &lt;p&gt;The three most interesting talks I attended:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://de.slideshare.net/ewolff/java-application-servers-are-dead&quot;&gt;Java App Servers are Dead&lt;/a&gt; by Eberhard Wolff. Contains some good arguments why deploying applications to app servers might not be the best idea. I especially liked the idea that the target application server is a dependency of your project (because you need a certain version) and dependencies should be explicit in you source tree.&lt;/li&gt;&lt;li&gt;Resilience with &lt;a href=&quot;https://github.com/Netflix/Hystrix&quot;&gt;Hystrix&lt;/a&gt; by Uwe Friedrichsen. A more advanced talk that was perfect for me because I had seen an introduction to fault tolerance by Uwe Friedrichsen at &lt;a href=&quot;http://www.xpdays.de&quot;&gt;XPDays Germany&lt;/a&gt; last year. Hystrix is a library I will definitively check out.&lt;/li&gt;&lt;li&gt;Log Managment with &lt;a href=&quot;http://graylog2.org/&quot;&gt;Graylog2&lt;/a&gt; by Lennart Koopmann. Graylog2 is a full application for doing log management that includes Elasticsearch, MongoDB and a Play application. Lennart mentioned some interesting numbers about installations, a system with an impressive scale.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Talks I would have liked to see:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://speakerdeck.com/stilkov/restful-http-on-the-jvm&quot;&gt;RESTful HTTP on the JVM&lt;/a&gt; by Martin Eigenbrodt and Stefan Tilkov&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://speakerdeck.com/timmo/micro-services-die-verheissungen-konnten-eintreten&quot;&gt;Micro Services&lt;/a&gt; by Timmo Freudl-Gierke&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;My Talks&lt;/h4&gt; &lt;p&gt;Surprisingly I had two talks accepted for BEDCon. I am especially glad that the more important talk on Search-Driven Applications with &lt;a href=&quot;http://twitter.com/tokraft&quot;&gt;Tobias&lt;/a&gt; of &lt;a href=&quot;http://exensio.de&quot;&gt;Exensio&lt;/a&gt; went really well and we were talking to a packed room. For me giving a talk in a team is far less stressful than giving a talk alone. I am looking forward to giving this talk again at &lt;a href=&quot;http://entwicklertag.de/karlsruhe/2014/vortrag/search-driven-applications&quot;&gt;Entwicklertag Karlsruhe in May&lt;/a&gt;. The &lt;a href=&quot;https://speakerdeck.com/exensio/search-driven-applications&quot;&gt;slides are available on Speaker Deck&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;My second short talk on Akka also went ok, the &lt;a href=&quot;http://www.florian-hopf.de/artikel-vortraege/akka-bedcon/&quot;&gt;slides are online&lt;/a&gt;. If you are interested in Akka you can also have a look at my blogposts on &lt;a href=&quot;http://blog.florian-hopf.de/2012/08/getting-rid-of-synchronized-using-akka.html&quot;&gt;message passing concurrency with Akka&lt;/a&gt; and &lt;a href=&quot;http://blog.florian-hopf.de/2013/10/cope-with-failure-actor-supervision-in.html&quot;&gt;supervision in Akka&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Tweets&lt;/h4&gt; &lt;p&gt;I know, &lt;a href=&quot;http://blog.florian-hopf.de/2014/03/javaland-2014-conference-talks-tweets.html&quot;&gt;I am repeating myself&lt;/a&gt;, but again I stored all the tweets for the conference in Elasticsearch so I can look at them using Kibana. The usual rules apply, each retweet counts as a seperate tweet. I am using a sharded version of the index so some of the counts might not be totally exact.&lt;/p&gt; &lt;h5&gt;Distribution&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-wyOb8uUqMx8/U0InzkW917I/AAAAAAAAAR0/yiKkKQnabVg/s1600/distribution-bedcon.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-wyOb8uUqMx8/U0InzkW917I/AAAAAAAAAR0/yiKkKQnabVg/s400/distribution-bedcon.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;There seem to be more tweets on the first day. I also had the impression that there were fewer people there for the second day at all. The first day has a spike that is probably related to the blackout at around 12.&lt;/p&gt; &lt;h5&gt;Hashtags&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-8xSBL6eLn7Y/U0In5qXdNEI/AAAAAAAAAR8/Y-aZyo19ciI/s1600/hashtags-bedcon.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-8xSBL6eLn7Y/U0In5qXdNEI/AAAAAAAAAR8/Y-aZyo19ciI/s400/hashtags-bedcon.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;This is a surprise: elasticsearch beats any other hashtag. logstash, kibana, springmvc, roca ... very specialized hashtags as well. As you can see from the numbers there weren't that many tweets at all.&lt;/p&gt; &lt;h5&gt;Mentions&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-Shqd9Jf6eFQ/U0Iowql-iHI/AAAAAAAAASI/Zp-lG8mBnwY/s1600/mentions-bedcon.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-Shqd9Jf6eFQ/U0Iowql-iHI/AAAAAAAAASI/Zp-lG8mBnwY/s400/mentions-bedcon.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;An even bigger surprise for me: I seem to have got mentioned a lot. After looking into this, this is caused by retweets counting as a mention as well. I had some tweets that got retweeted a few times.&lt;/p&gt; &lt;h4&gt;Negativity&lt;/h4&gt; &lt;p&gt;There is one thing that had me quite upset. During a short power failure (which was not related to BEDCon at all) I was watching a short talk. The speaker had nothing better to do than to insult the technicians that were there to help him. I don't get this attitude and I hope to never see this speaker again on any conference I am attending.&lt;/p&gt; &lt;p&gt;BEDCon is a great conference, all the people involved are doing a great job. I hope in the following years I can find the time to go there again.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>JavaLand 2014: The Conference, the Talks, the Tweets</title>
   <link href="http://blog.florian-hopf.de/2014/03/javaland-2014-conference-talks-tweets.html"/>
   <updated>2014-03-28T16:12:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/03/javaland-2014-conference-talks-tweets</id>
   <content type="html">&lt;p&gt;This week on Tuesday and Wednesday parts of the German Java community met for &lt;a href=&quot;http://javaland.eu&quot;&gt;JavaLand&lt;/a&gt;, the conference of the German Java community. In this post I would like to talk about the &lt;a href=&quot;#conference&quot;&gt;conference in general&lt;/a&gt;, &lt;a href=&quot;#talks&quot;&gt;some of the talks I attended&lt;/a&gt; and show you some facts about &lt;a href=&quot;#tweets&quot;&gt;what happened on Twitter&lt;/a&gt;.&lt;/p&gt; &lt;h4 id=&quot;conference&quot;&gt;Location and Organization&lt;/h4&gt; &lt;p&gt;The conference has the most unusual setting one can imagine: A theme park, &lt;a href=&quot;http://phantasialand.de&quot;&gt;Phantasialand in Brühl&lt;/a&gt;. Though I was rather sceptical about this it really turned out to be a good choice. The rooms for the talks were mostly well suited and it was a nice place for meeting outside. On Tuesday evening and at some times during the day some of the attractions were open for use. Though I am not really into theme parks I have to admit that some of them really were fun.&lt;/p&gt; &lt;p&gt;Another unusual choice of the organizers: There were no lunch breaks. Every talk started at the full hour and lasted for 45 minutes, starting from 09:00 in the morning and continuing to the evening. You had 15 minutes to change rooms and get a coffee. The attendees could decide by themselves if they would like to eat a really quick lunch in 15 minutes or skip one of the slots completely. This circumvents some of the problems with large queues at other conferences or breaks that are too long for some participants.&lt;/p&gt; &lt;p&gt;The quality of the food was excellent, with buffets during lunch time and even Tuesday evening. There were several main dishes, different variations of salads and dessert. One of the best conference catering I have ever seen.&lt;/p&gt; &lt;p&gt;There were quite some community events, e.g. the Java User Group café or the innovation lab with the Oculus Rift and a Quadrocopter.&lt;/p&gt; &lt;h4 id=&quot;talks&quot;&gt;The Talks&lt;/h4&gt; &lt;p&gt;Most of &lt;a href=&quot;http://www.javaland.eu/konferenz/2014-JL-konferenzplan.php&quot;&gt;the talks&lt;/a&gt; were in German but there were also some in English. I doubt that you get a lot of value if you don't speak German and go to JavaLand just for the talks, though there were several high profile non German speakers. On several partner stages companies presented talks done by their employees. I had an especially good impression of the &lt;a href=&quot;https://www.codecentric.de/&quot;&gt;Codecentric&lt;/a&gt; stage and regret that I couldn't go to see Patrick Peschlow talking on Elasticsearch, a talk I heard good things about.&lt;/p&gt; &lt;p&gt;Some of the talks had a common theme:&lt;/p&gt; &lt;h5&gt;Reactive Programming&lt;/h5&gt; &lt;p&gt;There was a surprising amount of talks on reactive programming. First Roland Kuhn, the project lead of the Akka project, introduced the principles of building &lt;a href=&quot;http://reactivemanifesto.org&quot;&gt;reactive applications&lt;/a&gt;. He showed how choosing an event driven approach can lead to scalable, resilient and responsive applications. He didn't go into technical details but only introduced the concepts.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-GbRTVqHnPDc/UzUli20q_CI/AAAAAAAAAQo/ZGohlXcQxGI/s1600/2014-03-25+10.24.33.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-GbRTVqHnPDc/UzUli20q_CI/AAAAAAAAAQo/ZGohlXcQxGI/s400/2014-03-25+10.24.33.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;At the end of day one, Michael Pisula gave a technical introduction into the actor framework &lt;a href=&quot;http://akka.io&quot;&gt;Akka&lt;/a&gt;, also referring to a lot of the concepts Roland Kuhn mentioned in the morning. Though I already have a good grasp of Akka there were some details that were really useful.&lt;/p&gt; &lt;p&gt;At the beginning of the second day &lt;a href=&quot;http://www.qualitects-group.com/&quot;&gt;Niko Köbler and Heiko Spindler&lt;/a&gt; gave another talk on reactive programming. It was a mixture of concepts and technical details, showing &lt;a href=&quot;https://www.meteor.com/&quot;&gt;MeteorJS&lt;/a&gt;, a framework for combining JavaScript on the client side with the server side, and again Akka.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-S4RCFnYzBRI/UzUoSTC3XUI/AAAAAAAAAQ8/TGreL_pbGIs/s1600/2014-03-26+09.18.30.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-S4RCFnYzBRI/UzUoSTC3XUI/AAAAAAAAAQ8/TGreL_pbGIs/s400/2014-03-26+09.18.30.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h5&gt;Profiling&lt;/h5&gt; &lt;p&gt;There were two sessions related to profiling: First Fabian Lange of Codecentric showing different aspects of profiling in general and some details on microbenchmarking. I will especially take a closer look on &lt;a href=&quot;http://openjdk.java.net/projects/code-tools/jmh/&quot;&gt;jmh&lt;/a&gt;, a microbenchmark tool in OpenJDK.&lt;/p&gt; &lt;p&gt;In &quot;JVM and application bottleneck troubleshooting with simple tools&quot; Daniel Witkowski of Azul demoed an example process of analyzing a problematic application. He used system tools and &lt;a href=&quot;http://visualvm.java.net/&quot;&gt;VisualVM&lt;/a&gt; to find and identify problems. A rather basic talk but nevertheless it is good to keep some of the practices in mind.&lt;/p&gt; &lt;h5&gt;Building Web Applications&lt;/h5&gt; &lt;p&gt;Felix Müller and Nils Wloka presented two different web frameworks. &lt;a href=&quot;http://playframework.org&quot;&gt;Play&lt;/a&gt;, presented by Felix Müller, is a framework that is mostly developed by Typesafe, the company behind Scala and Akka. You can use it from Java as well as Scala though the templates are always written in Scala. I started building an application using Play a while ago and had a quite good impression. If I had a use case for a full stack framework I would definitively have another look.&lt;/p&gt; &lt;p&gt;Nils Wloka did a session that obviously wasn't for everyone: 45 minutes of live coding in Clojure building a complete web application for voting on conference talks using &lt;a href=&quot;https://github.com/ring-clojure/ring&quot;&gt;Ring&lt;/a&gt;, &lt;a href=&quot;https://github.com/weavejester/compojure&quot;&gt;Compojure&lt;/a&gt; and &lt;a href=&quot;https://github.com/weavejester/hiccup&quot;&gt;Hiccup&lt;/a&gt;. I am currently working on a simple application using the same stack so I could follow at least most of his explanations. I especially liked that he managed to finish the app, deploy it to OpenShift and used it to let the audience vote on his talk. Quite impressive.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-f_T8GSOq1bs/UzUn5Q_ngDI/AAAAAAAAAQ4/yxKPYZ94Smk/s1600/2014-03-26+16.04.07.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-f_T8GSOq1bs/UzUn5Q_ngDI/AAAAAAAAAQ4/yxKPYZ94Smk/s400/2014-03-26+16.04.07.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;I like it that both frameworks support rapid development. You don't need to restart the server as often as with common Java web development. A lot of the changes are available instantly.&lt;/p&gt; &lt;h5&gt;Miscellaneous&lt;/h5&gt; &lt;p&gt;There were two more talks I'd like to mention: On the second day Patrick Baumgartner gave an excellent introduction into &lt;a href=&quot;http://neo4j.org&quot;&gt;Neo4J&lt;/a&gt;, the graph database on the JVM. He showed several use cases (including a graph of Whiskey brands that spawned quite a discussion on the properties of Whiskey in the audience) and some of the technical details. Though I just recently attended a long talk on Neo4J at &lt;a href=&quot;http://jug-ka.de&quot;&gt;JUG Karlsruhe&lt;/a&gt; and already had seen a similar talk by Patrick at &lt;a href=&quot;http://bed-con.org&quot;&gt;BEDCon&lt;/a&gt; last year it was really entertaining and good for refreshing some of the knowledge.&lt;/p&gt; &lt;p&gt;Finally the highlight of the conference for me: On the first day Stefan Tilkov gave one of his excellent talks on architecture: He showed how splitting applications and building for replacement instead of building for reuse can lead to cleaner applications. He gave several examples of companies that had employed these principles. I have never attended a bad talk by Stefan so if he's giving a talk at a conference you are at you should definitively go see it.&lt;/p&gt; &lt;h4 id=&quot;tweets&quot;&gt;The Tweets&lt;/h4&gt; &lt;p&gt;As I have &lt;a href=&quot;http://blog.florian-hopf.de/2013/11/devoxx-in-tweets.html&quot;&gt;done before for Devoxx&lt;/a&gt; and other conferences I tracked all of the tweets with the hashtags #javaland and #javaland14, stored them in &lt;a href=&quot;http://elasticsearch.org&quot;&gt;Elasticsearch&lt;/a&gt; and therefore had the possibility to analyze them with &lt;a href=&quot;http://www.elasticsearch.org/overview/kibana/&quot;&gt;Kibana&lt;/a&gt;. I started tracking several months before the conference but I am only showing some of my findings for the conference days here, as those can give good insight into which topics were hot. Each retweet counts as a separate tweet so if there is one tweet that gets retweeted a lot this will have a strong impact on the numbers.&lt;/p&gt; &lt;h5&gt;Timing&lt;/h5&gt; &lt;p&gt;Looking at the distribution of the tweets for the two conference days we can see that there are a lot of tweets in the morning of the first day and surprisingly in the evening of the first day. I suspect those are mostly tweets by people announcing that they are now starting to &lt;a href=&quot;http://nighthacking.com/java-8-javaland-roller-coaster/&quot;&gt;ride the Black Mamba&lt;/a&gt;. Of course this might also be related to the start of the Java 8 launch event but I like the Black Mamba theory better.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-X4zBRg49d2A/UzUpz64Yc3I/AAAAAAAAARM/d8STkw8yNSU/s1600/distribution.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-X4zBRg49d2A/UzUpz64Yc3I/AAAAAAAAARM/d8STkw8yNSU/s400/distribution.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h5&gt;Mentions&lt;/h5&gt; &lt;p&gt;A good incication of popular speakers are the mentions. It's a huge surprise that the twitter handle for the conference only is at third place. The two speakers Arun Gupta and David Blevins seem to be rather popular on twitter (or they just had a really long discussion).&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-7g7hq05zEoo/UzUp_0mef5I/AAAAAAAAARU/GjKx1fZyiMU/s1600/mentions.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-7g7hq05zEoo/UzUp_0mef5I/AAAAAAAAARU/GjKx1fZyiMU/s400/mentions.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h5&gt;Hashtags and Common Terms&lt;/h5&gt; &lt;p&gt;To see the trends we can have a look at the hashtags people used. I excluded #javaland as it is contained in every tweet anyway.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-_EiJWLPtIEA/UzUqGU44q-I/AAAAAAAAARc/P2ZlYJ8wPhM/s1600/hashtags.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-_EiJWLPtIEA/UzUqGU44q-I/AAAAAAAAARc/P2ZlYJ8wPhM/s400/hashtags.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The Java 8 launch event is dominant but Java EE 7 and JavaFX both are strong as well. There was quite some interest for &lt;a href=&quot;http://nighthacking.com/&quot;&gt;Stephen Chin and his Nighthacking sessions&lt;/a&gt;. Neo4J and Asciidoctor are quite a surprise (but not #wegotbeer, a reasonable hashtag).&lt;/p&gt; &lt;p&gt;Hashtags are one thing but we can also look at all the terms in the text of the tweets. I excluded a long list of common words so this is my curated list of the important terms.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-qY6HE-QxpoA/UzUqLLBlHEI/AAAAAAAAARk/xhN5L7YDO1k/s1600/terms.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-qY6HE-QxpoA/UzUqLLBlHEI/AAAAAAAAARk/xhN5L7YDO1k/s400/terms.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;&quot;Great&quot;, &quot;thanks&quot; and &quot;cool&quot; ... I am not that deep into statistics but it seems to me that people liked the conference.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;JavaLand was a fantastic conference. I had some doubts about the setting in the theme park but it was really great. I will definitively go there again next year, if you are in the area you should think about going there as well. Thanks to all organizers for doing a great job.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Book Review: Instant Apache Solr for Indexing Data How-to</title>
   <link href="http://blog.florian-hopf.de/2014/03/book-review-instant-apache-solr-for.html"/>
   <updated>2014-03-21T15:25:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/03/book-review-instant-apache-solr-for</id>
   <content type="html">&lt;p&gt;Indexing, the process of putting data in a search engine, often is the foundation of anything when building a search based application. With &lt;a href=&quot;http://www.packtpub.com/apache-solr-for-indexing-data/book&quot;&gt;Instant Apache Solr Indexing Data Howto&lt;/a&gt; Alexandre Rafalovitch has written a book dedicated to different aspects of indexing.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-gmfZOxEO6oU/Uyvo3PoWT8I/AAAAAAAAAQU/ID_2cJi-AAE/s1600/instant-apache-solr-indexing.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-gmfZOxEO6oU/Uyvo3PoWT8I/AAAAAAAAAQU/ID_2cJi-AAE/s200/instant-apache-solr-indexing.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The book is written in a cookbook style with tasks that are solved using different features of Apache Solr. Each task is classified with a difficulty level (simple, intermediate, advanced). The book shows you how to work with collections, index text and binary content (using http and Java) and how to use the Data Import Handler. You will learn about the difference between soft and hard commits, how the UpdateRequestProcessor works (showing the useful &lt;a href=&quot;http://wiki.apache.org/solr/ScriptUpdateProcessor&quot;&gt;ScriptUpdateProcessor&lt;/a&gt;) and the functionality of atomic updates. The final task describes an approach to index content in multiple languages.&lt;/p&gt; &lt;p&gt;Though it is quite short the book contains some really useful information. As it is dedicated to indexing alone you can't really use it for learning how to work with all aspects of Apache Solr but you can get some bang for the buck for you daily work. The only thing that I missed in the book is a more detailed description of more filters and tokenizers. Nevertheless you get quite some value from the book for a reasonable price.&lt;/p&gt; &lt;p&gt;If you are looking for more in-depth information I recommend &lt;a href=&quot;http://www.packtpub.com/apache-solr-3-enterprise-search-server/book&quot;&gt;Apache Solr 3 Enterprise Search Server&lt;/a&gt; (which covers an older version) or the recently finished &lt;a href=&quot;http://www.manning.com/grainger/&quot;&gt;Solr in Action&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Building a Navigation from a Search Index</title>
   <link href="http://blog.florian-hopf.de/2014/03/building-navigation-from-search-index.html"/>
   <updated>2014-03-14T15:09:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/03/building-navigation-from-search-index</id>
   <content type="html">&lt;p&gt;Though the classic use case for search engines is keyword search nowadays they are often used to serve content that doesn't resemble a search result list at all. As they are optimized for read access they make good candidates for serving any part of a website that needs a dynamic query mechanism. Faceting is most often used to display filters for refining the search results but it can also be used to build hierarchical navigations from results. I am using &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Solr&lt;/a&gt; in this post but the concept can also be used with Elasticsearch or even plain Lucene.&lt;/p&gt; &lt;h4&gt;Browsing Search Results&lt;/h4&gt;   &lt;p&gt;What we are trying to achieve can often be seen on Ecommerce sites but is also useful for serving content. You will be familiar with the navigation that &lt;a href=&quot;http://amazon.com&quot;&gt;Amazon&lt;/a&gt; provides: You can browse the categories that are displayed in a hierarchical navigation.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-ru7Y0O9bGMY/UyKl5geMfXI/AAAAAAAAAQA/qAq-VqfPXZY/s1600/amazon.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-ru7Y0O9bGMY/UyKl5geMfXI/AAAAAAAAAQA/qAq-VqfPXZY/s400/amazon.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Of course I am not familiar with the implementation details of how they are storing their content but search engines can be used to build something like this. You can index different types (editiorial content and products) and tag those with categories. The navigation and the page content is then built from the search results that are returned.&lt;/p&gt; &lt;p&gt;For a simple example we are indexing just products, two books and one record. Two Books by Haruki Murakami are in the category Books/Fiction and Books/Non-Fiction. The record by 13 &amp;amp; God is in the category Music/Downbeat. The resulting navigation should then be something like this:&lt;/p&gt; &lt;ul&gt;    &lt;li&gt;Books         &lt;ul&gt;            &lt;li&gt;Non-Fiction                 &lt;ul&gt;                    &lt;li&gt;Haruki Murakami&lt;/li&gt;                &lt;/ul&gt;            &lt;/li&gt;            &lt;li&gt;Fiction                 &lt;ul&gt;                    &lt;li&gt;Haruki Murakami&lt;/li&gt;                &lt;/ul&gt;            &lt;/li&gt;         &lt;/ul&gt;    &lt;/li&gt;    &lt;li&gt;Music         &lt;ul&gt;            &lt;li&gt;Downbeat                 &lt;ul&gt;                    &lt;li&gt;13 &amp;amp; God&lt;/li&gt;                &lt;/ul&gt;            &lt;/li&gt;        &lt;/ul&gt;    &lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;PathHierarchyTokenizer&lt;/h4&gt; &lt;p&gt;Lucene provides the &lt;a href=&quot;http://lucene.apache.org/core/4_0_0/analyzers-common/org/apache/lucene/analysis/path/PathHierarchyTokenizer.html&quot;&gt;PathHierarchyTokenizer&lt;/a&gt; that can be used to split path like hierarchies. It takes a path as input and creates segments from it. For example when indexing &lt;code&gt;Books/Non-Fiction/Haruki Murakami&lt;/code&gt; it will emit the following tokens:&lt;/p&gt; &lt;ul&gt;    &lt;li&gt;Books&lt;/li&gt;    &lt;li&gt;Books/Non-Fiction&lt;/li&gt;    &lt;li&gt;Books/Non-Fiction/Haruki Murakami&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;What is important: It doesn't just split the string on a delimiter but creates a real hierarchy with all the parent paths. This can be used to build our navigation.&lt;/p&gt; &lt;h4&gt;Solr&lt;/h4&gt; &lt;p&gt;Let's see an example with Solr. The configuration and a unit test is also available on &lt;a href=&quot;https://github.com/fhopf/solr-examples/tree/master/navigation&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;We are using a very simple schema with documents that only contain a title and a category&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;fields&amp;gt;&lt;br /&gt;    &amp;lt;field name=&amp;quot;title&amp;quot; type=&amp;quot;string&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;true&amp;quot; required=&amp;quot;true&amp;quot; multiValued=&amp;quot;false&amp;quot; /&amp;gt; &lt;br /&gt;    &amp;lt;field name=&amp;quot;category&amp;quot; type=&amp;quot;category&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/fields&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The title is just a string field but the category is a custom field that uses the &lt;a href=&quot;https://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.PathHierarchyTokenizerFactory&quot;&gt;PathHierarchyTokenizerFactory&lt;/a&gt;.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;fieldType name=&amp;quot;category&amp;quot; class=&amp;quot;solr.TextField&amp;quot; positionIncrementGap=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;analyzer type=&amp;quot;index&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;tokenizer class=&amp;quot;solr.PathHierarchyTokenizerFactory&amp;quot; delimiter=&amp;quot;/&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/analyzer&amp;gt;&lt;br /&gt;    &amp;lt;analyzer type=&amp;quot;query&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;tokenizer class=&amp;quot;solr.KeywordTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/analyzer&amp;gt;&lt;br /&gt;&amp;lt;/fieldType&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;When indexing data the path is split according to the rules of the PathHierarchyTokenizer. When querying we are taking the query term as it is so we can have exact matches.&lt;/p&gt; &lt;p&gt;Suppose we are now indexing three documents that are in the three categories we have seen above:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;URL=http://localhost:8082/solr/update/json&lt;br /&gt;curl $URL -H 'Content-type:application/json' -d '&lt;br /&gt;[&lt;br /&gt;  {&quot;title&quot;:&quot;What I Talk About When I Talk About Running&quot;, &quot;category&quot;:&quot;Books/Non-Fiction/Haruki Murakami&quot;}, &lt;br /&gt;  {&quot;title&quot;:&quot;South of the Border, West of the Sun&quot;, &quot;category&quot;:&quot;Books/Fiction/Haruki Murakami&quot;}, &lt;br /&gt;  {&quot;title&quot;:&quot;Own Your Ghost&quot;, &quot;category&quot;:&quot;Music/Downbeat/13&amp;amp;God&quot;}&lt;br /&gt;]'&lt;br /&gt;curl &quot;$URL?commit=true&quot;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We can easily request the navigation using facets. We query on all documents but return no documents at all. A facet is returned that contains our navigation structure:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl http://localhost:8082/solr/collection1/select?q=*%3A*&amp;amp;rows=0&amp;amp;wt=json&amp;amp;indent=true&amp;amp;facet=true&amp;amp;facet.field=category&lt;br /&gt;{&lt;br /&gt;  &quot;responseHeader&quot;:{&lt;br /&gt;    &quot;status&quot;:0,&lt;br /&gt;    &quot;QTime&quot;:1},&lt;br /&gt;  &quot;response&quot;:{&quot;numFound&quot;:3,&quot;start&quot;:0,&quot;docs&quot;:[]&lt;br /&gt;  },&lt;br /&gt;  &quot;facet_counts&quot;:{&lt;br /&gt;    &quot;facet_queries&quot;:{},&lt;br /&gt;    &quot;facet_fields&quot;:{&lt;br /&gt;      &quot;category&quot;:[&lt;br /&gt;        &quot;Books&quot;,2,&lt;br /&gt;        &quot;Books/Fiction&quot;,1,&lt;br /&gt;        &quot;Books/Fiction/Haruki Murakami&quot;,1,&lt;br /&gt;        &quot;Books/Non-Fiction&quot;,1,&lt;br /&gt;        &quot;Books/Non-Fiction/Haruki Murakami&quot;,1,&lt;br /&gt;        &quot;Music&quot;,1,&lt;br /&gt;        &quot;Music/Downbeat&quot;,1,&lt;br /&gt;        &quot;Music/Downbeat/13&amp;amp;God&quot;,1]},&lt;br /&gt;    &quot;facet_dates&quot;:{},&lt;br /&gt;    &quot;facet_ranges&quot;:{}}}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;When displaying the navigation you can now simply split the paths according to their hierarchies. The queries that are executed for displaying the content contain a filter query that filters the currently selected navigation item.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl &quot;http://localhost:8082/solr/collection1/select?q=*%3A*&amp;amp;wt=json&amp;amp;fq=category:Books&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;responseHeader&quot;:{&lt;br /&gt;    &quot;status&quot;:0,&lt;br /&gt;    &quot;QTime&quot;:27},&lt;br /&gt;  &quot;response&quot;:{&quot;numFound&quot;:2,&quot;start&quot;:0,&quot;docs&quot;:[&lt;br /&gt;      {&lt;br /&gt;        &quot;title&quot;:&quot;What I Talk About When I Talk About Running&quot;},&lt;br /&gt;      {&lt;br /&gt;        &quot;title&quot;:&quot;South of the Border, West of the Sun&quot;}]&lt;br /&gt;  }}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Using &lt;a href=&quot;http://wiki.apache.org/solr/SimpleFacetParameters#Tagging_and_excluding_Filters&quot;&gt;tags and exclusions&lt;/a&gt; you can even build the navigation using the same request that queries for the filtered documents.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl &quot;http://localhost:8082/solr/collection1/select?q=*%3A*&amp;amp;wt=json&amp;amp;fq=%7B%21tag%3Dcat%7Dcategory:Books&amp;amp;facet=true&amp;amp;facet.field=%7B%21ex%3Dcat%7Dcategory&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;responseHeader&quot;:{&lt;br /&gt;    &quot;status&quot;:0,&lt;br /&gt;    &quot;QTime&quot;:2},&lt;br /&gt;  &quot;response&quot;:{&quot;numFound&quot;:2,&quot;start&quot;:0,&quot;docs&quot;:[&lt;br /&gt;      {&lt;br /&gt;        &quot;title&quot;:&quot;What I Talk About When I Talk About Running&quot;},&lt;br /&gt;      {&lt;br /&gt;        &quot;title&quot;:&quot;South of the Border, West of the Sun&quot;}]&lt;br /&gt;  },&lt;br /&gt;  &quot;facet_counts&quot;:{&lt;br /&gt;    &quot;facet_queries&quot;:{},&lt;br /&gt;    &quot;facet_fields&quot;:{&lt;br /&gt;      &quot;category&quot;:[&lt;br /&gt;        &quot;Books&quot;,2,&lt;br /&gt;        &quot;Books/Fiction&quot;,1,&lt;br /&gt;        &quot;Books/Fiction/Haruki Murakami&quot;,1,&lt;br /&gt;        &quot;Books/Non-Fiction&quot;,1,&lt;br /&gt;        &quot;Books/Non-Fiction/Haruki Murakami&quot;,1,&lt;br /&gt;        &quot;Music&quot;,1,&lt;br /&gt;        &quot;Music/Downbeat&quot;,1,&lt;br /&gt;        &quot;Music/Downbeat/13&amp;God&quot;,1]},&lt;br /&gt;    &quot;facet_dates&quot;:{},&lt;br /&gt;    &quot;facet_ranges&quot;:{}}}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;If the navigation you are retrieving from the search engine is your single source of truth you might also have to add a sort mechanism; by default all facets are sorted by their count. This works in our simple case but not for the real world. To have them sorted in a defined way you can add numeric identifiers. You would then index paths like &lt;code&gt;100|Books/110|Non-Fiction/111|Haruki Murakami&lt;/code&gt; and request alphanumeric sorting via &lt;a href=&quot;http://wiki.apache.org/solr/SimpleFacetParameters#facet.sort&quot;&gt;facet.sort=index&lt;/a&gt;. When displaying the result you just remove the front of the string.&lt;/p&gt; &lt;p&gt;As you now are using the search engine to build the navigation you will immediately have the benefits of its filtering mechanisms. Only use categories that have online articles? Add a filter query &lt;code&gt;fq=online:true&lt;/code&gt;. Make sure that there are no categories with products that are out of stock? &lt;code&gt;fq=inStock:true&lt;/code&gt;.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Search engines offer great flexibility when delivering content. A lot of their functionality can be used to build applications that pull most of their content from the index.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Prefix and Suffix Matches in Solr</title>
   <link href="http://blog.florian-hopf.de/2014/03/prefix-and-suffix-matches-in-solr.html"/>
   <updated>2014-03-07T14:25:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/03/prefix-and-suffix-matches-in-solr</id>
   <content type="html">&lt;p&gt;Search engines are all about looking up strings. The user enters a query term that is then retrieved from the inverted index. Sometimes a user is looking for a value that is only a substring of values in the index and the user might be interested in those matches as well. This is especially important for languages like German that contain compound words like Semmelknödel where Knödel means dumpling and Semmel specializes the kind.&lt;/p&gt; &lt;h4&gt;Wildcards&lt;/h4&gt; &lt;p&gt;For demoing the approaches I am using a very simple schema. Documents consist of a text field and an id. The configuration as well as a unit test is also vailable on &lt;a href=&quot;https://github.com/fhopf/solr-examples/tree/master/prefix-suffix-match&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;fields&amp;gt;&lt;br /&gt;    &amp;lt;field name=&amp;quot;id&amp;quot; type=&amp;quot;string&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;true&amp;quot; required=&amp;quot;true&amp;quot; multiValued=&amp;quot;false&amp;quot; /&amp;gt;&lt;br /&gt;    &amp;lt;field name=&amp;quot;text&amp;quot; type=&amp;quot;text_general&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/fields&amp;gt;&lt;br /&gt;&amp;lt;uniqueKey&amp;gt;id&amp;lt;/uniqueKey&amp;gt;&lt;br /&gt;&amp;lt;types&amp;gt;&lt;br /&gt;    &amp;lt;fieldType name=&amp;quot;string&amp;quot; class=&amp;quot;solr.StrField&amp;quot; sortMissingLast=&amp;quot;true&amp;quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;fieldType name=&amp;quot;text_general&amp;quot; class=&amp;quot;solr.TextField&amp;quot; positionIncrementGap=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;analyzer&amp;gt;&lt;br /&gt;            &amp;lt;tokenizer class=&amp;quot;solr.StandardTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;filter class=&amp;quot;solr.LowerCaseFilterFactory&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;/analyzer&amp;gt;&lt;br /&gt;    &amp;lt;/fieldType&amp;gt;&lt;br /&gt;&amp;lt;/types&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;One approach that is quite popular when doing prefix or suffix matches is to use wildcards when querying. This can be done programmatically but you need to take care that any user input is then escaped correctly. Suppose you have the term &lt;em&gt;dumpling&lt;/em&gt; in the index and a user enters the term &lt;em&gt;dump&lt;/em&gt;. If you want to make sure that the query term matches the document in the index you can just add a wildcard to the user query in the code of your application so the resulting query then would be &lt;em&gt;dump*&lt;/em&gt;&lt;/p&gt; &lt;p&gt;Generally you should be careful when doing too much magic like this: if a user is in fact looking for documents containing the word dump she might not be interested in documents containing dumpling. You need to decide for yourself if you would like to have only matches the user is interested in (precision) or show the user as many probable matches as possible (recall). This heavily depends on the use cases for your application.&lt;/p&gt; &lt;p&gt;You can increase the user experience a bit by boosting exact matches for your term. You need to create a more complicated query but this way documents containing an exact match will score higher:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;dump^2 OR dump*&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;When creating a query like this you should also take care that the user can't add terms that will make the query invalid. The SolrJ method &lt;a href=&quot;http://lucene.apache.org/solr/4_2_1/solr-solrj/org/apache/solr/client/solrj/util/ClientUtils.html#escapeQueryChars%28java.lang.String%29&quot;&gt;&lt;code&gt;escapeQueryChars&lt;/code&gt; of the class ClientUtils&lt;/a&gt; can be used to escape the user input.&lt;/p&gt; &lt;p&gt;If you are now taking suffix matches into account the query can get quite complicated and creating a query like this on the client side is not for everyone. Depending on your application another approach can be the better solution: You can create another field containing NGrams during indexing.&lt;/p&gt; &lt;h4&gt;Prefix Matches with NGrams&lt;/h4&gt; &lt;p&gt;NGrams are substrings of your indexed terms that you can put in an additional field. Those substrings can then be used for lookups so there is no need for any wildcards. Using the (e)dismax handler you can automatically set a boost on your field that is used for exact matches so you get the same behaviour we have seen above.&lt;/p&gt; &lt;p&gt;For prefix matches we can use the &lt;a href=&quot;http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.EdgeNGramFilterFactory&quot;&gt;EdgeNGramFilter&lt;/a&gt; that is configured for an additional field:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;...&lt;br /&gt;    &amp;lt;field name=&amp;quot;text_prefix&amp;quot; type=&amp;quot;text_prefix&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;...&lt;br /&gt;    &amp;lt;copyField source=&amp;quot;text&amp;quot; dest=&amp;quot;text_prefix&amp;quot;/&amp;gt;&lt;br /&gt;...    &lt;br /&gt;    &amp;lt;fieldType name=&amp;quot;text_prefix&amp;quot; class=&amp;quot;solr.TextField&amp;quot; positionIncrementGap=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;analyzer type=&amp;quot;index&amp;quot;&amp;gt;&lt;br /&gt;            &amp;lt;tokenizer class=&amp;quot;solr.LowerCaseTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;filter class=&amp;quot;solr.EdgeNGramFilterFactory&amp;quot; minGramSize=&amp;quot;3&amp;quot; maxGramSize=&amp;quot;15&amp;quot; side=&amp;quot;front&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;/analyzer&amp;gt;&lt;br /&gt;        &amp;lt;analyzer type=&amp;quot;query&amp;quot;&amp;gt;&lt;br /&gt;            &amp;lt;tokenizer class=&amp;quot;solr.LowerCaseTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;/analyzer&amp;gt;&lt;br /&gt;    &amp;lt;/fieldType&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;During indexing time the text field value is copied to the &lt;code&gt;text_prefix&lt;/code&gt; field and analyzed using the EdgeNGramFilter. Grams are created for any length between 3 and 15, starting from the front of the string. When indexing the term &lt;em&gt;dumpling&lt;/em&gt; this would be&lt;/p&gt; &lt;ul&gt;&lt;li&gt;dum&lt;/li&gt;&lt;li&gt;dump&lt;/li&gt;&lt;li&gt;dumpl&lt;/li&gt;&lt;li&gt;dumpli&lt;/li&gt;&lt;li&gt;dumplin&lt;/li&gt;&lt;li&gt;dumpling&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;During query time the term is not split again so that the exact match for the substring can be used. As usual, the analyze view of the Solr admin backend can be a great help for seeing the analyzing process in action.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-LoUrVqzCydU/UxljLtj_xII/AAAAAAAAAPs/D1pFzs12JYE/s1600/analyze.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-LoUrVqzCydU/UxljLtj_xII/AAAAAAAAAPs/D1pFzs12JYE/s640/analyze.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Using the dismax handler you can now pass in the user query as it is and just advice it to search on your fields by adding the parameter &lt;code&gt;qf=text^2,text_prefix&lt;/code&gt;.&lt;/p&gt; &lt;h4&gt;Suffix Matches&lt;/h4&gt; &lt;p&gt;With languages that have compound words it's a common requirement to also do suffix matches. If a user queries for the term &lt;em&gt;Knödel&lt;/em&gt; (dumpling) it is expected that documents that contain the term&lt;em&gt;Semmelknödel&lt;/em&gt; also match.&lt;/p&gt; &lt;p&gt;Using Solr versions up to 4.3 this is no problem. You can use the EdgeNGramFilterFactory to create grams starting from the back of the string.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;...&lt;br /&gt;    &amp;lt;field name=&amp;quot;text_suffix&amp;quot; type=&amp;quot;text_suffix&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;...    &lt;br /&gt;    &amp;lt;copyField source=&amp;quot;text&amp;quot; dest=&amp;quot;text_suffix&amp;quot;/&amp;gt;&lt;br /&gt;...&lt;br /&gt;    &amp;lt;fieldType name=&amp;quot;text_suffix&amp;quot; class=&amp;quot;solr.TextField&amp;quot; positionIncrementGap=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;analyzer type=&amp;quot;index&amp;quot;&amp;gt;&lt;br /&gt;            &amp;lt;tokenizer class=&amp;quot;solr.StandardTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;filter class=&amp;quot;solr.LowerCaseFilterFactory&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;filter class=&amp;quot;solr.EdgeNGramFilterFactory&amp;quot; minGramSize=&amp;quot;3&amp;quot; maxGramSize=&amp;quot;15&amp;quot; side=&amp;quot;back&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;/analyzer&amp;gt;&lt;br /&gt;        &amp;lt;analyzer type=&amp;quot;query&amp;quot;&amp;gt;&lt;br /&gt;            &amp;lt;tokenizer class=&amp;quot;solr.KeywordTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;filter class=&amp;quot;solr.LowerCaseFilterFactory&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;/analyzer&amp;gt;&lt;br /&gt;    &amp;lt;/fieldType&amp;gt;&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This creates suffixes of the indexed term that also contains the term &lt;em&gt;knödel&lt;/em&gt; so our query works.&lt;/p&gt; &lt;p&gt;But, using more recent versions of Solr you will encounter a problem during indexing time:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;java.lang.IllegalArgumentException: Side.BACK is not supported anymore as of Lucene 4.4, use ReverseStringFilter up-front and afterward&lt;br /&gt;    at org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter.&lt;init&gt;(EdgeNGramTokenFilter.java:114)&lt;br /&gt;    at org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter.&lt;init&gt;(EdgeNGramTokenFilter.java:149)&lt;br /&gt;    at org.apache.lucene.analysis.ngram.EdgeNGramFilterFactory.create(EdgeNGramFilterFactory.java:52)&lt;br /&gt;    at org.apache.lucene.analysis.ngram.EdgeNGramFilterFactory.create(EdgeNGramFilterFactory.java:34)&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can't use the EdgeNGramFilterFactory anymore for suffix ngrams. But fortunately the stack trace also advices us how to fix the problem. We have to combine it with ReverseStringFilter:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;fieldType name=&amp;quot;text_suffix&amp;quot; class=&amp;quot;solr.TextField&amp;quot; positionIncrementGap=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;analyzer type=&amp;quot;index&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;tokenizer class=&amp;quot;solr.LowerCaseTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;filter class=&amp;quot;solr.ReverseStringFilterFactory&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;filter class=&amp;quot;solr.EdgeNGramFilterFactory&amp;quot; minGramSize=&amp;quot;3&amp;quot; maxGramSize=&amp;quot;15&amp;quot; side=&amp;quot;front&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;filter class=&amp;quot;solr.ReverseStringFilterFactory&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/analyzer&amp;gt;&lt;br /&gt;    &amp;lt;analyzer type=&amp;quot;query&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;tokenizer class=&amp;quot;solr.LowerCaseTokenizerFactory&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/analyzer&amp;gt;&lt;br /&gt;&amp;lt;/fieldType&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This will now yield the same results as before.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Whether you are going for manipulating your query by adding wildcards or if you should be using the NGram approach heavily depends on your use case and is also a matter of taste. Personally I am using NGrams most of the time as disk space normally isn't a concern for the kind of projects I am working on. Wildcard search has become a lot faster in Lucene 4 so I doubt there is a real benefit there anymore. Nevertheless I tend to do as much processing I can during indexing time.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Introduction to the ODF Toolkit</title>
   <link href="http://blog.florian-hopf.de/2014/02/introduction-to-odf-toolkit.html"/>
   <updated>2014-02-27T14:11:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/02/introduction-to-odf-toolkit</id>
   <content type="html">&lt;p&gt;Microsoft Office has been the dominating office suite and unfortunately it still is. For a long time not only the programs were closed but also the file format.&lt;/p&gt; &lt;h4&gt;Open Document&lt;/h4&gt; &lt;p&gt;Nevertheless there are open alternatives available, most notable Libre Office/Apache OpenOffice.org. In 2005 the OASIS foundation standardized &lt;a href=&quot;https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office&quot;&gt;Open Document&lt;/a&gt;, an open alternative to the proprietary world of Microsoft. Open Document is heavily influenced by the OpenOffice.org file format but is supported by multiple office suites and viewers.&lt;/p&gt; &lt;p&gt;Open Document files are zip files that contain some XML documents. You can go ahead and unzip any documents you might have:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;unzip -l aufwaende-12.ods &lt;br /&gt;Archive:  aufwaende-12.ods&lt;br /&gt;  Length      Date    Time    Name&lt;br /&gt;---------  ---------- -----   ----&lt;br /&gt;       46  2012-12-31 15:16   mimetype&lt;br /&gt;      815  2012-12-31 15:16   meta.xml&lt;br /&gt;     8680  2012-12-31 15:16   settings.xml&lt;br /&gt;   171642  2012-12-31 15:16   content.xml&lt;br /&gt;     3796  2012-12-31 15:16   Thumbnails/thumbnail.png&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/images/Bitmaps/&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/popupmenu/&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/toolpanel/&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/statusbar/&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/progressbar/&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/toolbar/&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/menubar/&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/accelerator/current.xml&lt;br /&gt;        0  2012-12-31 15:16   Configurations2/floater/&lt;br /&gt;    22349  2012-12-31 15:16   styles.xml&lt;br /&gt;      993  2012-12-31 15:16   META-INF/manifest.xml&lt;br /&gt;---------                     -------&lt;br /&gt;   208321                     16 files&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The &lt;code&gt;mimetype&lt;/code&gt; file determines what kind of document it is (in this case &lt;code&gt;application/vnd.oasis.opendocument.spreadsheet&lt;/code&gt;), &lt;code&gt;META-INF/manifest.xml&lt;/code&gt; lists the files in the archive. The most important file is &lt;code&gt;content.xml&lt;/code&gt; that contains the body of the document.&lt;/p&gt; &lt;h4&gt;Server Side Processing&lt;/h4&gt; &lt;p&gt;Though there are quite some viewers and editors for Open Document available when it comes to the server side the situation used to be different. For processing Microsoft Office files there is the Java library &lt;a href=&quot;http://poi.apache.org/&quot;&gt;Apache POI&lt;/a&gt;, which provides a lot of functionality to read and manipulate Microsoft Office files. But if you wanted to process Open Document files nearly your only option was to install OpenOffice.org on the server and talk to it by means of its &lt;a href=&quot;http://en.wikipedia.org/wiki/Universal_Network_Objects&quot;&gt;UNO API&lt;/a&gt;. Not exactly an easy thing to do.&lt;/p&gt; &lt;h4&gt;ODF Toolkit&lt;/h4&gt; &lt;p&gt;Fortunately there is light at the end of the tunnel: the &lt;a href=&quot;http://incubator.apache.org/odftoolkit/index.html&quot;&gt;ODF Toolkit&lt;/a&gt; project, currently incubating at Apache, provides lightweight access to files in the Open Document format from Java. As the name implies it's a toolkit, consisting of multiple projects.&lt;/p&gt;   &lt;p&gt;The heart of it is the schema generator that ingests the Open Document specification that is available as a RelaxNG schema. It provides a template based facility to generate files from the ODF specification. Currently it only generates Java classes but it can also be used to create different files (think of documentation or accessors for different programming languages).&lt;/p&gt; &lt;p&gt;The next layer of the toolkit is &lt;a href=&quot;http://incubator.apache.org/odftoolkit/odfdom/index.html&quot;&gt;ODFDOM&lt;/a&gt;. It provides templates that generate classes for DOM access of elements and attributes of ODF documents. Additionally it provides facilities like packaging and document encryption.&lt;/p&gt; &lt;p&gt;For example, you can list the file paths of an ODF document using the ODFPackage class:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;OdfPackage pkg = OdfPackage.loadPackage(&quot;aufwaende-12.ods&quot;);&lt;br /&gt;Set&lt;String&gt; filePaths = pkg.getFilePaths();&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;If you are familiar with the Open Document spec ODFDOM will be the only library you need. But if you are like most of us and don't know all the elements and attributes by heart there is another project for you: Simple API provides easy access to a lot of the features you might expect from a library like this: You can deal with higher level abstractions like paragraphs for text or rows and cells in the spreadsheet world or search for and replace text.&lt;/p&gt; &lt;p&gt;This code snippet creates a spreadsheet, adds some cells to it and saves it:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;SpreadsheetDocument doc = SpreadsheetDocument.newSpreadsheetDocument();&lt;br /&gt;Table sheet = doc.getSheetByIndex(0);&lt;br /&gt;sheet.getCellByPosition(0, 0).setStringValue(&quot;Betrag&quot;);&lt;br /&gt;sheet.getCellByPosition(1, 0).setDoubleValue(23.0);&lt;br /&gt;doc.save(File.createTempFile(&quot;odf&quot;, &quot;.ods&quot;));&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;Code&lt;/h4&gt; &lt;p&gt;If you are interested in seeing more code using the ODF Toolkit you can have a look at the &lt;a href=&quot;http://incubator.apache.org/odftoolkit/simple/document/cookbook/index.html&quot;&gt;cookbook&lt;/a&gt; that contains a lot of useful code snippets for the Simple API. Additionally you should keep an eye on this blog for the second part of the series where we will look at an application that extracts data from spreadsheets.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Book Review: Search-Based Applications</title>
   <link href="http://blog.florian-hopf.de/2014/02/book-review-search-based-applications.html"/>
   <updated>2014-02-20T14:55:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/02/book-review-search-based-applications</id>
   <content type="html">&lt;p&gt;Search is moving away from the simple keyword search box with a result list for indexed web pages. Features like facetting and aggregations offer completely new possibilities for data discovery, making them relevant for business applications as well.&lt;/p&gt; &lt;h4&gt;The Book&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://searchbasedapplications.com/&quot;&gt;Search-Based Applications&lt;/a&gt; by Greogory Grefenstette and Laura Wilber describes the changes that have occured in the last years regarding search engines, traditionally used for indexing web pages and databases, that have been used for business applications.&lt;/p&gt; &lt;p&gt;The short book introduces the motivation for using search engines in business applications, mostly caused by exponential data growth and realtime needs. Several chapters describe what has changed in the database and search engine world, focusing on one aspect in each chapter. On the search side it shows that advanced features like faceted search or natural language processing techniques can be valuable for offering real time access on data that has traditionally been put to a data warehouse. On the database side it shows that with the advent of non-relational types, some databases are moving in the direction of the flexible schema, scalability or specialized access patterns of search engines.&lt;/p&gt; &lt;p&gt;Some common themes in the book for using search based applications are the aggregation of content from different data sources and the reduction of load on databases by offloading the traffic to the read optimized search engines. Mixing content from different data sources can be useful to provide flexible access on multiple legacy systems, increasing usability of the applications. The document model of search engines and the possibility to do incremental indexing lead to applications that provide near realtime access to data and can be adjusted to match changing needs quicker.&lt;/p&gt; &lt;p&gt;Though most of the book is product agnostic one chapter lists some platforms that are available for building search based applications, mainly focusing on big commercial players like Exalead (the company of the authors), Endeca and Autonomy. The book closes with three case studies that show different aspects of building search based applications.&lt;/p&gt; &lt;p&gt;Even if there are some statements contained that I don't fully agree with or that are even contradictory it is a very good book for understanding the reasoning behind building search based applications. I got some new ideas for applications of search engines and this alone makes it a worthwile read.&lt;/p&gt; &lt;h4&gt;Open Source Options for Search Based Applications&lt;/h4&gt; &lt;p&gt;Though the book lists quite some SBA platforms and related technology there is not a single mention of &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Apache Solr&lt;/a&gt;, which is quite surprising as it employs a lot of the features the authors define for SBAs. Solr has the Data Import Handler to connect external data sources, semantic technologies (though probably not as rich as some of the commercial options) and complementary open source projects like carrot² for search result clustering or ManifoldCF as a connector framework.&lt;/p&gt; &lt;p&gt;When the book talks about replacing parts of data warehouse applications with SBAs for real time analytics this of course reminds me of use cases for &lt;a href=&quot;http://elasticsearch.org&quot;&gt;Elasticsearch&lt;/a&gt;. &lt;a href=&quot;http://www.elasticsearch.org/overview/kibana/&quot;&gt;Kibana&lt;/a&gt; or custom dashboards can make a wealth of information that is contained in the index accessible in an easy way.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Search Meetups in Germany</title>
   <link href="http://blog.florian-hopf.de/2014/02/search-meetups-in-germany.html"/>
   <updated>2014-02-13T14:35:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/02/search-meetups-in-germany</id>
   <content type="html">&lt;p&gt;I enjoy going to user group events. Not only because of the talks that are an integral part of most meetups but also to meet and chat with likeminded people.&lt;/p&gt; &lt;p&gt;Fortunately there are some user groups in Germany that are focused on search technology, a topic I am especially interested in. This post lists those I know, if there is one I missed let me know in the comments. For reasons of suspense I am listing the groups from east to west.&lt;/p&gt; &lt;h4&gt;Elasticsearch User Group Berlin&lt;/h4&gt; &lt;p&gt;Berlin has the luxury of a &lt;a href=&quot;http://www.meetup.com/ElasticSearch-UG-Berlin/&quot;&gt;usergroup dedicated to Elasticsearch&lt;/a&gt; only. The group is organized by people of Trifork who are seasoned event organizers. The group seems to have a surprising success with regular meetings and up to 50 participants. This is probably caused by the high startup density in Berlin, the ease of use and scalability of Elasticsearch makes it very popular among them.&lt;/p&gt; &lt;h4&gt;Search Meetup Munich&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.meetup.com/Search-Meetup-Munich/&quot;&gt;Search Meetup Munich&lt;/a&gt; is a very active group organized by Alexander Reelsen of Elasticsearch. There are bimonthly meetings at alternating companies with 2 to 3 talks per event. Topics are open source search in general with a strong emphasis on Lucene, Solr and Elasticsearch. Most speakers will give the talk in English if there are people in the audience who don't speak German. The amount of participants ranges from 20 - 40 people. I am surprised about the vital community in Munich with a lot of startups doing interesting things with search. Though it is quite a way from Karlsruhe to Munich I try to attend the meetings as often as I can.&lt;/p&gt; &lt;h4&gt;Solr Lucene User Group Deutschland e.V.&lt;/h4&gt; &lt;p&gt;Though the name implies it's a national group &lt;a href=&quot;http://www.slug-de.org/&quot;&gt;Solr Lucene User Group Deutschland e.V.&lt;/a&gt; is located in Augsburg. It seems to be mainly organized by members of &lt;a href=&quot;http://www.shi-gmbh.com/&quot;&gt;SHI GmbH&lt;/a&gt;, a prominent Lucidworks and Elasticsearch partner. The &lt;a href=&quot;http://www.meetup.com/SLUG-Solr-Lucene-User-Group-Deutschland-e-V/&quot;&gt;meetup page&lt;/a&gt; is rather quite so far with one event last year with one participant.&lt;/p&gt; &lt;h4&gt;Search Meetup Frankfurt&lt;/h4&gt; &lt;p&gt;The first &lt;a href=&quot;http://searchmeetupfrankfurt.de/&quot;&gt;search meetup&lt;/a&gt; I attended with around 10 participants, a talk on the indexing pipeline of the Solr based product search solution &lt;a href=&quot;http://www.searchperience.de/home.html&quot;&gt;Searchperience&lt;/a&gt; and some discussions. There are quite some people with non-Java background doing PHP web development. Unfortunately the 2012 event I attended seems to be the last event that happened. I don't take that personally.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-9WzoTb6iNsY/UvxornZQlnI/AAAAAAAAAPc/vol1CjA87f0/s1600/smka_social_gplus_cropped_small.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-9WzoTb6iNsY/UvxornZQlnI/AAAAAAAAAPc/vol1CjA87f0/s1600/smka_social_gplus_cropped_small.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Search Meetup Karlsruhe&lt;/h4&gt; &lt;p&gt;Last but not least: As I probably can't travel to Munich all the time and I would like to have some exchange with locals I just started &lt;a href=&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/&quot;&gt;Search Meetup Karlsruhe&lt;/a&gt; together with &lt;a href=&quot;http://www.exensio.de/&quot;&gt;Exensio&lt;/a&gt;, long time Solr users and Elasticsearch partners. I don't expect it to be as huge as Munich or Berlin but I hope we can start some interesting discussions.&lt;/p&gt; &lt;p&gt;We just scheduled our &lt;a href=&quot;http://www.meetup.com/Search-Meetup-Karlsruhe/events/161417512/&quot;&gt;first meeting&lt;/a&gt; with two talks on Linked Data Search and the difference between building applications based on databases vs. search engines. If you are in the area and interested in search you should join us.&lt;/p&gt; &lt;h4 id=&quot;elasticsearchStuttgart&quot;&gt;elasticsearch.Stuttgart (Update 16.02.2014)&lt;/h4&gt; &lt;p&gt;Just a day after publishing this post another &lt;a href=&quot;http://www.meetup.com/Elasticsearch-Stuttgart/&quot;&gt;Elasticsearch Meetup was announced, this time in Stuttgart&lt;/a&gt;. The first event is scheduled for March 25 with an &lt;a href=&quot;http://www.meetup.com/Elasticsearch-Stuttgart/events/166289382/&quot;&gt;Elasticsearch 1.0 release party including a talk by Alexander Reelsen&lt;/a&gt;. If this didn't clash with &lt;a href=&quot;http://www.javaland.eu/&quot;&gt;JavaLand conference&lt;/a&gt; I would definitively go there but I hope there will be more events in the future I can attend.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Elasticsearch is Distributed by Default</title>
   <link href="http://blog.florian-hopf.de/2014/02/elasticsearch-is-distributed-by-default.html"/>
   <updated>2014-02-07T15:00:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/02/elasticsearch-is-distributed-by-default</id>
   <content type="html">&lt;p&gt;One of the big advantages Elasticsearch has over Solr is that it is really easy to get started with. You can download it, start it, index and search immediately. Schema discovery and the JSON based REST API all make it a very beginner friendly tool.&lt;/p&gt; &lt;p&gt;Also, another aspect, Elasticsearch is distributed by default. You can add nodes that will automatically be discovered and your index can be distributed across several nodes.&lt;/p&gt; &lt;p&gt;The distributed nature is great to get started with but you need to be aware that there are some consequences. Distribution comes with a cost. In this post I will show you how relevancy of search results might be affected by sharding in Elasticsearch.&lt;/p&gt; &lt;h4&gt;Relevancy&lt;/h4&gt; &lt;p&gt;As Elasticsearch is based on Lucene it also uses its relevancy algorithm by default, called TF/IDF. Term frequency (the amount of terms in a document) and the frequency of the term in an index (IDF) are important parts of the relevancy function. You can see &lt;a href=&quot;http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/search/similarities/TFIDFSimilarity.html&quot;&gt;details of the default formula in the Lucene API docs&lt;/a&gt; but for this post it is sufficient to know that the more often a term occurs in a document the more relevant it is considered. Terms that are more frequent in the index are considered less relevant.&lt;/p&gt; &lt;h4&gt;A Problematic Example&lt;/h4&gt; &lt;p&gt;Let's see the problem in action. We are starting with a fresh Elasticsearch instance and index some test documents. The documents only consist of one field that has the same text in it:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST http://localhost:9200/testindex/doc/0 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;0&quot;,&quot;_version&quot;:1}&lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/1 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;1&quot;,&quot;_version&quot;:1}&lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/2 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;2&quot;,&quot;_version&quot;:1}&lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/3 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;3&quot;,&quot;_version&quot;:1}&lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/4 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;4&quot;,&quot;_version&quot;:1}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;When we search for those documents by text they of course are returned correctly.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/testindex/doc/_search?q=title:hut&amp;pretty=true&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;took&quot; : 4,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;max_score&quot; : 0.10848885,&lt;br /&gt;    &quot;hits&quot; : [ {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;4&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.10848885, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;0&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.10848885, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;1&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.10848885, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;2&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.10848885, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;3&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.10848885, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now, let's index five more documents that are similar to the first documents but contain our test term &lt;code&gt;Hut&lt;/code&gt; only once.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST http://localhost:9200/testindex/doc/5 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;5&quot;,&quot;_version&quot;:1} &lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/6 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;6&quot;,&quot;_version&quot;:1}&lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/7 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;7&quot;,&quot;_version&quot;:1}&lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/8 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;8&quot;,&quot;_version&quot;:1}&lt;br /&gt;curl -XPOST http://localhost:9200/testindex/doc/9 -d '{ &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;9&quot;,&quot;_version&quot;:1}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;As the default relevancy formula takes the term frequency in a document into account those documents should score less than our original documents. So if we query for &lt;code&gt;hut&lt;/code&gt; again the results still contain our original documents at the beginning:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/testindex/doc/_search?q=title:hut&amp;pretty=true&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;took&quot; : 6,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 10,&lt;br /&gt;    &quot;max_score&quot; : 0.2101998,&lt;br /&gt;    &quot;hits&quot; : [ {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;4&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.2101998, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      [...]&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;3&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.2101998, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;9&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.1486337, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      [...]&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;8&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.1486337, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We are still happy. The most relevant documents are at the top of our search results. Now let's index something that is completely different from our original documents:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST http://localhost:9200/testindex/doc/10 -d '{ &quot;title&quot; : &quot;mayhem and chaos&quot; }'&lt;br /&gt;{&quot;ok&quot;:true,&quot;_index&quot;:&quot;testindex&quot;,&quot;_type&quot;:&quot;doc&quot;,&quot;_id&quot;:&quot;10&quot;,&quot;_version&quot;:1}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now, if we search again for our test term something strange will happen:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/testindex/doc/_search?q=title:hut&amp;pretty=true&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;took&quot; : 2,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 10,&lt;br /&gt;    &quot;max_score&quot; : 0.35355338,&lt;br /&gt;    &quot;hits&quot; : [ {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;3&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.35355338, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;8&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.25, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat er nicht&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;testindex&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;doc&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;4&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.2101998, &quot;_source&quot; : { &quot;title&quot; : &quot;Mein Hut der hat vier Ecken, 4 Ecken hat mein Hut&quot; }&lt;br /&gt;    }, {&lt;br /&gt;      [...]&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Though the document we indexed last has nothing to do with our original documents it influenced our search and one of the documents that should score less is now the second result. This is something that you wouldn't expect. The behavior is caused by the default sharding of Elasticsearch that distributes a logical Elasticsearch index across several Lucene indices.&lt;/p&gt; &lt;h4&gt;Sharding&lt;/h4&gt; &lt;p&gt;When you are starting a single instance and index some documents Elasticsearch will by default create five shards under the hood, so there are five Lucene indices. Each of those shards contains some of the documents you are adding to the index. The assignment of documents to a shard happens in a way so that the documents will be distributed evenly.&lt;/p&gt; &lt;p&gt;You can get information about the shards and their document counts using the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html&quot;&gt;indices status API&lt;/a&gt; or more visually appealing using one of the plugins, e.g. &lt;a href=&quot;http://mobz.github.io/elasticsearch-head/&quot;&gt;elasticsearch-head&lt;/a&gt;. There are five shards for our index, once we click on a shard we can see further details about the shard, including the doc count.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-25GBK86P4ks/UvR1xmM2UKI/AAAAAAAAAPE/L6XoL_STcRo/s1600/shards-and-docs.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-25GBK86P4ks/UvR1xmM2UKI/AAAAAAAAAPE/L6XoL_STcRo/s640/shards-and-docs.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;   &lt;p&gt;If you check the shards right after you indexed the first five documents you will notice that those are distributed evenly across all shards. Each shard contains one of the documents. The second batch is again distributed evenly. The final document we index creates some imbalance. One shard will have an additional document.&lt;/p&gt; &lt;h4&gt;The Effects on Relevancy&lt;/h4&gt; &lt;p&gt;Each shard in Elasticsearch is a Lucene index in itself and as an index in Elasticsearch consists of multiple shards it needs to distribute the queries across multiple Lucene indices. Especially the inverse document frequency is difficult to calculate in this case.&lt;/p&gt; &lt;p&gt;Reconsider the Lucene relevancy formula: the term frequency as well as the inverse document frequency are important. When indexing the original 5 documents all documents had the same term frequency as well as the same idf for our term. The next documents still had no impact on the idf as each document in the index still contained the term.&lt;/p&gt; &lt;p&gt;Now, when indexing the last document something potentially unexpected is happening. The new document is added to one of the shards. On this shard we therefore changed the inverse document frequency which is calculated from all the documents that contain the term but also takes the overall document count in the Lucene index into account. On the shard that contains the new document we increased the idf value as now there are more documents in the Lucene index. As idf has quite some weight on the overall relevancy score we &quot;boosted&quot; the documents of the Lucene index that now contains more documents.&lt;/p&gt; &lt;p&gt;If you'd like to see details on the relevancy calculation you can use the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-explain.html&quot;&gt;explain API&lt;/a&gt; or simply add a parameter &lt;code&gt;explain=true&lt;/code&gt;. This will not only tell you all the details of the results of the relevancy function for each document but also which shard a document resides on. It can give you really useful information when debugging relevancy problems.&lt;/p&gt; &lt;h4&gt;How to Fix It?&lt;/h4&gt; &lt;p&gt;When beginning with Elasticsearch you might fix this by setting the your index to use one shard only. Though this will work it is not a good idea: Sharding is a very powerful feature of Elasticsearch and you shouldn't give it up on it easily. If you notice that there are problems with your relevancy that are caused by these issues you should rather try to use the &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-search-type.html&quot;&gt;&lt;code&gt;search_type&lt;/code&gt;&lt;/a&gt; &lt;code&gt;dfs_query_then_fetch&lt;/code&gt; instead of the default &lt;code&gt;query_then_fetch&lt;/code&gt;. The difference between those is that dfs queries all the document frequencies of the shards in advance. This way Elasticsearch can calculate the overall document frequency and all results will be in the correct order:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/testindex/doc/_search?q=title:hut&amp;pretty=true&amp;explain=true&amp;search_type=dfs_query_then_fetch&quot;&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Though the example we have seen here is artificially constructed this is something that can occur and I have already seen in live applications. The behaviour can especially be relevant when there are either very few documents or your documents are distributed to the shards in an unfortunate way. It is great that Elasticsearch makes distributed searches as easy and as performant as possible but you need to be aware that you might not get exact hits.&lt;/p&gt; &lt;p&gt;Zachary Thong has written a &lt;a href=&quot;http://www.elasticsearch.org/blog/understanding-query-then-fetch-vs-dfs-query-then-fetch/&quot;&gt;blog post about this behavior as well at the Elasticsearch blog&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Proxying Solr</title>
   <link href="http://blog.florian-hopf.de/2014/01/proxying-solr.html"/>
   <updated>2014-01-30T15:53:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/01/proxying-solr</id>
   <content type="html">&lt;p&gt;The dominant deployment model for Solr is running it as a standalone webapp. You can use it in embedded mode in Java but then you are missing some of the goodies like the seperate JVM (your GC will thank you for it) and you are of course tied to Java then.&lt;/p&gt; &lt;p&gt;Most of the time Solr is considered similar to a database; only custom webapp code can talk to it and it is not exposed to the net. In your webapp you are then using any of the client libraries to access Solr and build your queries.&lt;/p&gt; &lt;a href=&quot;http://1.bp.blogspot.com/-6dLkAi5gGSA/Uuno12cD3-I/AAAAAAAAAOo/0PtBGlcLffY/s1600/webapp.png&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-6dLkAi5gGSA/Uuno12cD3-I/AAAAAAAAAOo/0PtBGlcLffY/s400/webapp.png&quot; /&gt;&lt;/a&gt; &lt;p&gt;With the rise of JavaScript on the client side sometimes people get the idea to put Solr to the web directly. There is no custom webapp layer in between, only the web talking to Solr.&lt;/p&gt; &lt;a href=&quot;http://4.bp.blogspot.com/-ttLLOEBjk1Y/UunpCs9iOiI/AAAAAAAAAOw/J9xd8KJGea8/s1600/proxy.png&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-ttLLOEBjk1Y/UunpCs9iOiI/AAAAAAAAAOw/J9xd8KJGea8/s400/proxy.png&quot; /&gt;&lt;/a&gt; &lt;p&gt;A proxy needs to sit in front of the Solr server that only allows certain requests. You won't allow any requests that are potentially modifying your index or do any other harm. This can be done but you need to be aware of some things:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;You need to take extra care to only expose request handlers and parameters that you need.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://issues.apache.org/jira/browse/SOLR-2854&quot;&gt;Bugs or features in Solr might expose more functionality than you expect.&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://groups.google.com/d/msg/ajax-solr/zhrG-CncrRE/HsyRwmR4mEsJ&quot;&gt;Denial of Service attacks are easier to do.&lt;/a&gt;&lt;/li&gt;&lt;li&gt;The client side logic can get more complicated though there are libs like &lt;a href=&quot;https://github.com/evolvingweb/ajax-solr&quot;&gt;AJAX Solr&lt;/a&gt; available.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Most of the time putting Solr directly to the web is not an option, but you can, if you are willing to take some risk. I think that especially the possibility of DOS attacks shouldn't be taken lightly. The more flexibility you want to have on the query side the more care needs to be taken to secure the system. If you'd like to do it anyway see &lt;a href=&quot;http://charlesnagy.info/it/best-practices/incredibly-fast-solr-autosuggest&quot;&gt;this post on how to use nginx as a proxy to Solr&lt;/a&gt; and &lt;a href=&quot;https://github.com/evolvingweb/ajax-solr/wiki/Solr-proxies&quot;&gt;this list of specialized proxies for Solr&lt;/a&gt;. For general instructions on &lt;a href=&quot;http://wiki.apache.org/solr/SolrSecurity&quot;&gt;securing your Solr server see the project wiki&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Analyze your Maven Project Dependencies with dependency:analyze</title>
   <link href="http://blog.florian-hopf.de/2014/01/analyze-your-maven-project-dependencies.html"/>
   <updated>2014-01-23T15:27:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/01/analyze-your-maven-project-dependencies</id>
   <content type="html">&lt;p&gt;When working on a larger Maven project it might happen that you lose track of the dependecies in your project. Over time you are adding new dependencies, remove code or move code to modules so some of the dependencies become obsolete. Though I did lots of Maven projects I have to admit I didn't know until recently that the dependency plugin contains a useful goal for solving this problem: &lt;code&gt;dependency:analyze&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;The &lt;a href=&quot;http://maven.apache.org/plugins/maven-dependency-plugin/analyze-mojo.html&quot;&gt;dependency:analyze&lt;/a&gt; mojo can find dependencies that are declared for your project but are not necessary. Additionally it can find dependecies that are used but are undeclared, which happens when you are directly using transitive dependencies in your code.&lt;/p&gt; &lt;h4&gt;Analyzing Dependencies&lt;/h4&gt; &lt;p&gt;I am showing an example with the &lt;a href=&quot;http://incubator.apache.org/odftoolkit/&quot;&gt;Odftoolkit&lt;/a&gt; project. It contains quite some dependencies and is old enough that some of them are outdated. ODFDOM is the most important module of the project, providing low level access to the Open Document structure from Java code. Running &lt;code&gt;mvn dependency:tree&lt;/code&gt; we can see its dependencies at the time of writing:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;mvn dependency:tree&lt;br /&gt;[INFO] Scanning for projects...&lt;br /&gt;[INFO]                                                                         &lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] Building ODFDOM 0.8.10-incubating-SNAPSHOT&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] &lt;br /&gt;[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ odfdom-java ---&lt;br /&gt;[INFO] org.apache.odftoolkit:odfdom-java:jar:0.8.10-incubating-SNAPSHOT&lt;br /&gt;[INFO] +- org.apache.odftoolkit:taglets:jar:0.8.10-incubating-SNAPSHOT:compile&lt;br /&gt;[INFO] |  \- com.sun:tools:jar:1.7.0:system&lt;br /&gt;[INFO] +- xerces:xercesImpl:jar:2.9.1:compile&lt;br /&gt;[INFO] |  \- xml-apis:xml-apis:jar:1.3.04:compile&lt;br /&gt;[INFO] +- junit:junit:jar:4.8.1:test&lt;br /&gt;[INFO] +- org.apache.jena:jena-arq:jar:2.9.4:compile&lt;br /&gt;[INFO] |  +- org.apache.jena:jena-core:jar:2.7.4:compile&lt;br /&gt;[INFO] |  +- commons-codec:commons-codec:jar:1.5:compile&lt;br /&gt;[INFO] |  +- org.apache.httpcomponents:httpclient:jar:4.1.2:compile&lt;br /&gt;[INFO] |  +- org.slf4j:jcl-over-slf4j:jar:1.6.4:compile&lt;br /&gt;[INFO] |  +- org.apache.httpcomponents:httpcore:jar:4.1.3:compile&lt;br /&gt;[INFO] |  +- org.slf4j:slf4j-api:jar:1.6.4:compile&lt;br /&gt;[INFO] |  +- org.slf4j:slf4j-log4j12:jar:1.6.4:compile&lt;br /&gt;[INFO] |  \- log4j:log4j:jar:1.2.16:compile&lt;br /&gt;[INFO] +- org.apache.jena:jena-core:jar:tests:2.7.4:test&lt;br /&gt;[INFO] +- net.rootdev:java-rdfa:jar:0.4.2:compile&lt;br /&gt;[INFO] |  \- org.apache.jena:jena-iri:jar:0.9.1:compile&lt;br /&gt;[INFO] \- commons-validator:commons-validator:jar:1.4.0:compile&lt;br /&gt;[INFO]    +- commons-beanutils:commons-beanutils:jar:1.8.3:compile&lt;br /&gt;[INFO]    +- commons-digester:commons-digester:jar:1.8:compile&lt;br /&gt;[INFO]    \- commons-logging:commons-logging:jar:1.1.1:compile&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] BUILD SUCCESS&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] Total time: 1.877s&lt;br /&gt;[INFO] Finished at: Mon Jan 20 00:41:05 CET 2014&lt;br /&gt;[INFO] Final Memory: 13M/172M&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The project contains some direct dependencies with a lot of transitive dependencies. When running &lt;code&gt;mvn dependency:analyze&lt;/code&gt; on the project we will see that our dependencies don't seem to be correct:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;mvn dependency:analyze&lt;br /&gt;[INFO] Scanning for projects...&lt;br /&gt;[INFO]                                                                         &lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] Building ODFDOM 0.8.10-incubating-SNAPSHOT&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] &lt;br /&gt;[...] &lt;br /&gt;[INFO] &lt;&lt;&lt; maven-dependency-plugin:2.1:analyze (default-cli) @ odfdom-java &lt;&lt;&lt;&lt;br /&gt;[INFO] &lt;br /&gt;[INFO] --- maven-dependency-plugin:2.1:analyze (default-cli) @ odfdom-java ---&lt;br /&gt;[WARNING] Used undeclared dependencies found:&lt;br /&gt;[WARNING]    org.apache.jena:jena-core:jar:2.7.4:compile&lt;br /&gt;[WARNING]    xml-apis:xml-apis:jar:1.3.04:compile&lt;br /&gt;[WARNING] Unused declared dependencies found:&lt;br /&gt;[WARNING]    org.apache.odftoolkit:taglets:jar:0.8.10-incubating-SNAPSHOT:compile&lt;br /&gt;[WARNING]    org.apache.jena:jena-arq:jar:2.9.4:compile&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] BUILD SUCCESS&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] Total time: 4.769s&lt;br /&gt;[INFO] Finished at: Mon Jan 20 00:43:27 CET 2014&lt;br /&gt;[INFO] Final Memory: 28M/295M&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The second part of the warnings is easier to understand; we have declared some dependencies that we are never using, the taglets and jena-arq. When comparing this with the output we got above you will notice that the largest set of transitive dependencies was imported by the jena-arq dependency. And we don't even need it.&lt;/p&gt; &lt;p&gt;The first part seems to be more difficult: there are two used but undeclared dependencies found. What does it mean? Shouldn't compiling fail if there are any dependencies that are undeclared? No, it just means that we are directly using a transitive dependency from our code which we should better declare ourselves.&lt;/p&gt; &lt;h4&gt;Breaking the Build on Dependency Problems&lt;/h4&gt; &lt;p&gt;If you want to find problems with your dependencies as early as possible it's best to integrate the check in your build. The &lt;code&gt;dependency:analyze&lt;/code&gt; goal we have seen above is meant to be used in a standalone way, for automatic execution there is the &lt;a href=&quot;http://maven.apache.org/plugins/maven-dependency-plugin/analyze-only-mojo.html&quot;&gt;analyze-only mojo&lt;/a&gt;. It automatically binds to the &lt;code&gt;verify&lt;/code&gt; phase and can be declared like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;&lt;br /&gt;    &amp;lt;artifactId&amp;gt;maven-dependency-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;    &amp;lt;version&amp;gt;2.8&amp;lt;/version&amp;gt;&lt;br /&gt;    &amp;lt;executions&amp;gt;&lt;br /&gt;        &amp;lt;execution&amp;gt;&lt;br /&gt;            &amp;lt;id&amp;gt;analyze&amp;lt;/id&amp;gt;&lt;br /&gt;            &amp;lt;goals&amp;gt;&lt;br /&gt;                &amp;lt;goal&amp;gt;analyze-only&amp;lt;/goal&amp;gt;&lt;br /&gt;            &amp;lt;/goals&amp;gt;&lt;br /&gt;            &amp;lt;configuration&amp;gt;&lt;br /&gt;                &amp;lt;failOnWarning&amp;gt;true&amp;lt;/failOnWarning&amp;gt;&lt;br /&gt;                &amp;lt;outputXML&amp;gt;true&amp;lt;/outputXML&amp;gt;&lt;br /&gt;            &amp;lt;/configuration&amp;gt;&lt;br /&gt;        &amp;lt;/execution&amp;gt;&lt;br /&gt;    &amp;lt;/executions&amp;gt;&lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now the build will fail if there are any problems found. Conveniently, if an undeclared dependency has been found, it will also output the XML that you can then paste in your pom file.&lt;/p&gt; &lt;p&gt;A final word of caution: the default analyzer works on the bytecode level so in special cases it might not notice a dependency correctly, e.g. when you are using constants from a dependency that are inlined.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Geo-Spatial Features in Solr 4.2</title>
   <link href="http://blog.florian-hopf.de/2014/01/geo-spatial-features-in-solr-42.html"/>
   <updated>2014-01-17T22:42:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/01/geo-spatial-features-in-solr-42</id>
   <content type="html">&lt;p&gt;Last week I have shown how you can use the classic spatial support in Solr. It uses the &lt;code&gt;LatLonType&lt;/code&gt; to index locations that can then be used to query, filter or sort by distance. Starting with Solr 4.2 there is a &lt;a href=&quot;http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4&quot;&gt;new module&lt;/a&gt; available. It uses the &lt;a href=&quot;http://lucene.apache.org/core/4_0_0/spatial/&quot;&gt;Lucene Spatial module&lt;/a&gt; which is more powerful but also needs to be used differently. You can still use the old approach but in this post I will show you how to use the new features to do the same operations we saw last week.&lt;/p&gt; &lt;h4&gt;Indexing Locations&lt;/h4&gt; &lt;p&gt;Again we are indexing talks that contain a title and a location. For the new spatial support you need to add a different field type:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;fieldType name=&amp;quot;location_rpt&amp;quot; class=&amp;quot;solr.SpatialRecursivePrefixTreeFieldType&amp;quot;&lt;br /&gt;    distErrPct=&amp;quot;0.025&amp;quot;&lt;br /&gt;    maxDistErr=&amp;quot;0.000009&amp;quot;&lt;br /&gt;    units=&amp;quot;degrees&amp;quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Contrary to the &lt;code&gt;LatLonType&lt;/code&gt; the &lt;code&gt;SpatialRecursivePrefixTreeFieldType&lt;/code&gt; is no subfield type but stores the data structure itself. The attribute &lt;code&gt;maxDistErr&lt;/code&gt; determines the accuracy of the location, in this case it is 0.000009 degrees which is close to one meter and should be enough for most location searches.&lt;/p&gt; &lt;p&gt;To use the type in our documents of course we also need to add it as a field:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;field name=&amp;quot;location&amp;quot; type=&amp;quot;location_rpt&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;true&amp;quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now we are indexing some documents with three fields: the path (which is our id), the title of the talk and the location.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl http://localhost:8082/solr/update/json?commit=true -H 'Content-type:application/json' -d '&lt;br /&gt;[&lt;br /&gt; {&quot;path&quot; : &quot;1&quot;, &quot;title&quot; : &quot;Search Evolution&quot;, &quot;location&quot; : &quot;49.487036,8.458001&quot;},&lt;br /&gt; {&quot;path&quot; : &quot;2&quot;, &quot;title&quot; : &quot;Suchen und Finden mit Lucene und Solr&quot;, &quot;location&quot; : &quot;49.013787,8.419936&quot;}&lt;br /&gt;]'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Again, the location of the first document is Mannheim, the second Karlsruhe. You can see that the locations are encoded in an ngram-like fashion when looking at the schema browser in the administration backend:&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-fNpPsr60DXM/Uti6IvDPJFI/AAAAAAAAAOU/iBOfVoxrK1I/s1600/schema-browser.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-fNpPsr60DXM/Uti6IvDPJFI/AAAAAAAAAOU/iBOfVoxrK1I/s640/schema-browser.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Sorting by Distance&lt;/h4&gt; &lt;p&gt;A common use case is to sort the results by distance from a certain location. You can't use the Solr 3 syntax anymore but need to use a the &lt;code&gt;geofilt&lt;/code&gt; query parser that maps the distance to the score which you then sort on.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;http://localhost:8082/solr/select?q={!geofilt%20score=distance%20sfield=location%20pt=49.487036,8.458001%20d=100}&amp;sort=score asc&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;As the name implies the &lt;code&gt;geofilt&lt;/code&gt; query parser originally is for filtering. You need to pass in the distance that is used for filtering so by sorting you might also cause an impact on the results that are returned. For our example passing in a distance of 10 kilometers will only yield one result. This is something to be aware of.&lt;/p&gt; &lt;h4&gt;Filtering by Distance&lt;/h4&gt; &lt;p&gt;We can use the same approach we saw above to filter our results to only match talks in a given area. We can either use the geofilt query parser (that filters by radius) or the bbox query parser (which filters on a box around the radius). As you can imagine, the query looks similar: &lt;/p&gt; &lt;pre&gt;&lt;code&gt;http://localhost:8082/solr/select?q=*:*&amp;fq={!geofilt%20score=distance%20sfield=location%20pt=49.013787,8.419936%20d=10}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This will return all talks in a distance of 10 kilometers from Karlsruhe.&lt;/p&gt; &lt;h4&gt;Doing Fancy Stuff&lt;/h4&gt; &lt;p&gt;Besides the features we have looked at in this post you can also do more advanced stuff. In Solr 3 Spatial you can't have multivalued location fields, which is possible with Solr 4.2. Also now you can also index lines or polygons that can then be queried and intersected. In &lt;a href=&quot;http://people.apache.org/~hossman/spatial-for-non-spatial-meetup-20130117/&quot;&gt;this presentation&lt;/a&gt; Chris Hostetter uses this feature to determine overlapping of time, an interesting use case that you might not think of at first.&lt;/p&gt; </content>
 </entry>
 
 <entry>
   <title>Geo-Spatial Features in Solr 3</title>
   <link href="http://blog.florian-hopf.de/2014/01/geo-spatial-features-in-solr-3.html"/>
   <updated>2014-01-10T00:44:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/01/geo-spatial-features-in-solr-3</id>
   <content type="html">&lt;p&gt;Solr is mainly known for its full text search capabilities. You index text and are able to search it in lowercase or stemmed form, depending on your analyzer chain. But besides text Solr can do more: You can use RangeQueries to query numeric fields (&quot;Find all products with a price lower than 2€&quot;), do date arithmetic (&quot;Find me all news entries from last week&quot;) or do geospatial queries, which we will look at in this post. What I am describing here is the &lt;a href=&quot;http://wiki.apache.org/solr/SpatialSearch&quot;&gt;old spatial search support&lt;/a&gt;. Next week I will show you how to do the same things using recent versions of Solr.&lt;/p&gt; &lt;h4&gt;Indexing Locations&lt;/h4&gt; &lt;p&gt;Suppose we are indexing talks in Solr that contain a title and a location. We need to add the field type for locations to our schema:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;fieldType name=&amp;quot;location&amp;quot; class=&amp;quot;solr.LatLonType&amp;quot; subFieldSuffix=&amp;quot;_coordinate&amp;quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;LatLonType is a subfield type which means that it not only creates one field but also additional fields, one for longitude and one for latitude. The subFieldSuffix attribute determines the name of the field that will be &lt;code&gt;&amp;lt;fieldname&amp;gt;_&amp;lt;i&amp;gt;_&amp;lt;subFieldSuffix&amp;gt;&lt;/code&gt;. If the name of our field is location and we are indexing a latitude/longitude pair this would lead to three fields: &lt;code&gt;location, location_0_coordinate, location_1_coordinate&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;To use the type in our schema we need to add one field and one dynamic field definition for the sub fields:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;field name=&amp;quot;location&amp;quot; type=&amp;quot;location&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;dynamicField name=&amp;quot;*_coordinate&amp;quot; type=&amp;quot;tdouble&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;false&amp;quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The dynamic field is of type tdouble so we need to make sure that it is also available in our schema. The attributes indexed on location is special in this case: It determines if the subfields for the coordinates are created at all.&lt;/p&gt; &lt;p&gt;Let's index some documents. We are adding three fields, the path (which is our id), the title of the talk and the location.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl http://localhost:8983/solr/update/json?commit=true -H 'Content-type:application/json' -d '&lt;br /&gt;[&lt;br /&gt; {&quot;path&quot; : &quot;1&quot;, &quot;title&quot; : &quot;Search Evolution&quot;, &quot;location&quot; : &quot;49.487036,8.458001&quot;},&lt;br /&gt; {&quot;path&quot; : &quot;2&quot;, &quot;title&quot; : &quot;Suchen und Finden mit Lucene und Solr&quot;, &quot;location&quot; : &quot;49.013787,8.419936&quot;}&lt;br /&gt;]'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The location of the first document is Mannheim, the second Karlsruhe. We can see that our documents are indexed and that the location is stored by querying all documents:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl &quot;http://localhost:8983/solr/select?q=*%3A*&amp;wt=json&amp;indent=true&quot;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Looking at the schema browser we can also see that the two subfields have been created. Each contains the terms for the &lt;a href=&quot;http://mentaldetritus.blogspot.de/2013/01/grokking-solr-trie-fields.html&quot;&gt;Trie&lt;/a&gt; field.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-WHA3lzxncVY/Us7O4JfBk4I/AAAAAAAAAOE/boocZR5c4As/s1600/schema-browser.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-WHA3lzxncVY/Us7O4JfBk4I/AAAAAAAAAOE/boocZR5c4As/s640/schema-browser.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Sorting by Distance&lt;/h4&gt; &lt;p&gt;One use case you might have when indexing locations is to sort the results by distance from a certain location. This can for example be useful for classifieds or rentals to show the nearest results first.&lt;/p&gt; &lt;p&gt;Sorting can be done via the &lt;code&gt;geodist()&lt;/code&gt; function. We need to pass in the location that is used as a basis via the &lt;code&gt;pt&lt;/code&gt; parameter and the location field to use in the function via the &lt;code&gt;sfield&lt;/code&gt; parameter. We can see this in action by sorting twice, once for a location in Durlach near Karlsruhe and once for Heidelberg, which is near Mannheim:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl &quot;http://localhost:8983/solr/select?wt=json&amp;indent=true&amp;q=*:*&amp;sfield=location&amp;pt=49.003421,8.483133&amp;sort=geodist%28%29%20asc&quot;&lt;br /&gt;curl http://localhost:8983/solr/select?wt=json&amp;indent=true&amp;q=*:*&amp;sfield=location&amp;pt=49.399119,8.672479&amp;sort=geodist%28%29%20asc&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Both return the results in the correct order. You can also use the &lt;code&gt;geodist()&lt;/code&gt; function to boost results that are closer to your location. See the &lt;a href=&quot;http://wiki.apache.org/solr/SpatialSearch#How_to_boost_closest_results&quot;&gt;Solr wiki&lt;/a&gt; for details.&lt;/p&gt; &lt;h4&gt;Filtering by Distance&lt;/h4&gt; &lt;p&gt;Another common use case is to filter the search results to only show results from a certain area, e.g. in a distance of 10 kilometers. This can either be done automatically or via facets.&lt;/p&gt; &lt;p&gt;Filtering is done using another function, &lt;code&gt;geofilt()&lt;/code&gt;. It accepts the same parameters we have seen before but of course for filtering you add it as a filter query. The distance can be passed using the parameter &lt;code&gt;d&lt;/code&gt;, the unit defaults to kilometers. Suppose you are in Durlach and only want to see talk that are in a distance of 10 kilometers:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl &quot;http://localhost:8983/solr/select?wt=json&amp;indent=true&amp;q=*:*&amp;fq={!geofilt}&amp;pt=49.003421,8.483133&amp;sfield=location&amp;d=10&quot;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This only returns the result in Karlsruhe. Once we decide that we want to see results in a distance of 100 kilometers we again see both results:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl &quot;http://localhost:8983/solr/select?wt=json&amp;indent=true&amp;q=*:*&amp;fq={!geofilt}&amp;pt=49.003421,8.483133&amp;sfield=location&amp;d=100&quot;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Pretty useful! If you are interested, there is more on the &lt;a href=&quot;http://wiki.apache.org/solr/SpatialSearch&quot;&gt;Solr wiki&lt;/a&gt;. Next week I will show you how to do the same using the new spatial support in Solr versions starting from 4.2.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>20 Months of Freelancing</title>
   <link href="http://blog.florian-hopf.de/2014/01/20-months-of-freelancing.html"/>
   <updated>2014-01-01T16:40:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2014/01/20-months-of-freelancing</id>
   <content type="html">&lt;p&gt;It's now 20 months that I am working as a freelancer on my own. With the end of year thing going on it's time to look back on what happened and I would like to take the chance to write about what I did, what works and what I would like to do in the future.&lt;/p&gt; &lt;h4&gt;Why Freelancing?&lt;/h4&gt; &lt;p&gt;During my time at university I started working for a small consulting company that specialized in open source software. I was the first employee and started working part time but even paused my studies for half a year to work full time with them. The company grew and in 2006 after finally getting my degree I joined them full time. I always enjoyed the work and dedicated a lot of my energy and time. In 2012, with around 30 employees I noticed that I needed something else. I had already switched to a 4 day work week in 2011 to have more time for myself, to learn and experiment. Though the company is still great to work with it just didn't fit me anymore.&lt;/p&gt; &lt;p&gt;After a long time at a company that has partly been home and family it is difficult to just switch to another company. Also I wanted to have more control over what kind of projects I am doing and I liked to have some time on my own to write blog posts and do talks at user groups and conferences. I always spent time at customer projects and often liked it so it was an obvious decision to go with freelancing.&lt;/p&gt; &lt;h4&gt;The Start&lt;/h4&gt; &lt;p&gt;Before I quit I decided that I wanted to do more with search technologies. I had worked a lot on content management systems and search always is a crucial part. Having done several larger projects with Lucene and Solr and even the first Solr integration in OpenCms I knew that I had the necessary experience and that I liked it.&lt;/p&gt; &lt;p&gt;I had minimal savings when I quit and no customers so far. Other freelancers are often surprised when I tell this and advice to only quit when you already know who you will be working for next. I guess this was some kind of hubris, I was really determined that I wanted to do freelancing and knew that there were companies who needed my help.&lt;/p&gt; &lt;p&gt;I started freelancing in May and had already organized to give a talk at our &lt;a href=&quot;http://jug-ka.de&quot;&gt;local Java User Group&lt;/a&gt; on Lucene and Solr in July. I wanted to have the full month of May for talk preparation and bootstrapping the business, all the things like getting a website, getting an accountant and so on. Unfortunately I didn't find a project until the beginning of July with a lot of my savings already spent on living cost and necessary items for the business. Be aware that it will take you up to two months from the beginning of a project until you see the first money.&lt;/p&gt; &lt;h4&gt;Marketing&lt;/h4&gt; &lt;p&gt;The good thing about freelancing: I can call all the activities I like to do marketing and tell myself that those are necessary. The bad thing about it: I don't spend enough time on paid projects.&lt;/p&gt; &lt;p&gt;I am spending lots of my time that is not paid for on learning: Blog posts, books and conferences. I got to a quite frequent rhythm with weekly posts, spoke at several user groups and conferences and joined an open source project, &lt;a href=&quot;http://incubator.apache.org/odftoolkit/&quot;&gt;OdfToolkit&lt;/a&gt;. A lot of freelancers don't do any of those and dedicate all their time working on customer projects but those activities are part of the reason I went with freelancing.&lt;/p&gt; &lt;h4&gt;The Projects&lt;/h4&gt; &lt;p&gt;When talking about freelancing you probably think about sitting in the coffee shop, doing several projects in parallel. For me this is different, lots of Java projects are rather long term and require you to work in a team, which is best done on premise. Though I like the idea of doing more diverse projects I am also happy to have some stability. Having long term clients prevents some of the context switching involved with multiple projects and you have to spend less time on sales.&lt;/p&gt; &lt;p&gt;My first project involved working on an online shop for a large retailer built on Hybris, a commercial Ecommerce engine. I did a lot of Solr stuff and though it was rather stressful working on product search was really interesting. Also the people are nice.&lt;/p&gt; &lt;p&gt;Though I started with the intention of doing more search projects I am currently involved in a large CMS project for a retailer, (re)building parts of their online presence. Search only plays a minor part in it but I like working with the people, it's a great work atmosphere and some of the problems they face are really interesting. Before doing the project I had to think a lot whether I want to sign this long term contract but I am glad I did. Fortunately I still have time to do some short term consulting on the side (mostly single days, mostly Solr).&lt;/p&gt; &lt;h4&gt;Where Do I Get The Projects From?&lt;/h4&gt; &lt;p&gt;When starting I thought it would be a lot easier to get projects but customers are not exactly magically lining up to get my services. I try to avoid working with freelance agents, though a lot of Java projects are only possible to get through them. Most of the project inquiries I get directly are from people who know me from organizing the local Java User Group. I didn't start helping the user group for the marketing but I have to admit, it really paid of.&lt;/p&gt; &lt;p&gt;Besides that I am still working for customers of my old employer. They contact me with interesting projects and though of course they are taking their share I still earn enough for myself.&lt;/p&gt; &lt;p&gt;Most of the inquiries I get from agencies, mostly through my XING profile are for Hybris and CoreMedia, two of the commercial systems I did work in. I enjoy working with CoreMedia and could imagine to do projects with Hybris again but I would be far more happy if agencies contacted me for Lucene, Solr or Elasticsearch.&lt;/p&gt; &lt;p&gt;There have been some inquiries from people who found me through my blog but never something that was really doable (mostly overseas). Speaking at user groups and conferences has led to some contacts but never to a real project so far. So you could say that the marketing activities I spend most of my time on didn't pay off. But getting direct projects is not the only benefit of both of these activities. Those are also important for me for learning and growing.&lt;/p&gt; &lt;h4&gt;The Future&lt;/h4&gt; &lt;p&gt;Freelancing has been exactly the right choice for me. I managed to find projects where I can do my 4 day work week, leaving enough time for blogging, preparing talks and learning. I managed to do weekly blog posts for quite some months during the year, cut back a bit because it became overwhelming. Starting with the new year I hope I can get back to more frequent posts. Also, I'll be submitting talks to conferences again and hope that I can find more time to work on the OdfToolkit.&lt;/p&gt; &lt;p&gt;I'll be staying with my current client for as long as they need me but I am determined to only do search centric projects afterwards. Also I am planning to do a bit of work in other countries in Europe with a special twist. Watch this blog for the announcement.&lt;/p&gt; &lt;p&gt;When starting with freelancing you have a lot of questions and even simple things can take some time to find out on your own. I will compile a list of resources that helped me and publish those on my blog soon. If you are just starting with freelancing you are of course also welcome to contact me anytime. &lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Book Review: Taming Text</title>
   <link href="http://blog.florian-hopf.de/2013/12/book-review-taming-text.html"/>
   <updated>2013-12-18T14:58:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/12/book-review-taming-text</id>
   <content type="html">&lt;p&gt;This is text. As I presume you are human you will understand the words and their meaning. Some words have multiple meanings like the word like. Also as English isn't my native tongue there will be errors in my writing but you will understand it anyway. Our brain is doing a fantastic job at inferring meaning from the context. This is something that is far more difficult for machines.&lt;/p&gt; &lt;p&gt;Grant S. Ingersoll, Thomas S. Morton and Andrew L. Farris have written a book about all the difficulties that you might encounter when processing text with machines and ways to solve them. &lt;a href=&quot;http://www.manning.com/ingersoll/&quot;&gt;Taming Text&lt;/a&gt; not only shows you the theory of extracting, searching and classifying information in text but also introduces different open source projects that you can integrate in your application.&lt;/p&gt; &lt;p&gt;Each chapter focuses on one problem space and most of them can even be read in isolation. You will learn about the difficulties in understanding text, mostly caused by ambiguous meanings and the context words appear in. Tokenization and entity recognition are introduced with some basics of linguistics. Searching in text is covered well with all details on analyzing, the inverted index and the vector space model, which is also important for clustering and classification. Fuzzy string matching, the process of looking up similar strings, is shown using the famous Levenshtein distance, NGrams and Tries. A larger part of the book finally focuses on text clustering, the unsupervised process of putting documents into clusters, and classification and categorization, a learning process that needs some precategorized data.&lt;/p&gt; &lt;p&gt;Throughout all the chapters the authors introduce sample applications in Java using one or more of the open source projects that are covered. You will see an application that searches text in Mary Shelleys Frankenstein using &lt;a href=&quot;http://lucene.apache.org&quot;&gt;Apache Lucene&lt;/a&gt; and does entity recognition to identify people and places using &lt;a href=&quot;http://opennlp.apache.org/&quot;&gt;Apache OpenNLP&lt;/a&gt;. &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Apache Solr&lt;/a&gt; is mostly used for searching and OpenNLP can do extensive analysis of text like tokenization, determining sentences or parts of speech tagging. Content is extracted from different file formats using &lt;a href=&quot;http://tika.apache.org&quot;&gt;Apache Tika&lt;/a&gt;. Text clustering is shown using &lt;a href=&quot;http://project.carrot2.org/&quot;&gt;Carrot²&lt;/a&gt; for search result clustering in Solr, &lt;a href=&quot;http://mahout.apache.org&quot;&gt;Apache Mahout&lt;/a&gt; is mainly used for document clustering and classification with some help of Lucene, Solr and OpenNLP. The final example of the book builds on the knowledge of all the preceding chapters showing you an example question answering system similar to &lt;a href=&quot;http://en.wikipedia.org/wiki/Watson_%28computer%29&quot;&gt;IBM Watson&lt;/a&gt; that accepts natural language questions and tries to give correct answers from a data set extracted from Wikipedia.&lt;/p&gt; &lt;p&gt;This book is exceptional in that it covers many different topics but the authors manage to combine them in a coherent example. It is one of the books in &lt;a href=&quot;http://www.drdobbs.com/joltawards/jolt-awards-the-best-books/240162065?pgno=4&quot;&gt;this years Jolt award&lt;/a&gt; for a good reason. If you are doing anything with text, be it searching or analytics you are advised to get a copy for yourself. I know that I will come back to mine again in the future when I need to refresh some of the information.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Reindexing Content in Elasticsearch with stream2es</title>
   <link href="http://blog.florian-hopf.de/2013/11/reindexing-content-in-elasticsearch_27.html"/>
   <updated>2013-11-28T14:42:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/11/reindexing-content-in-elasticsearch_27</id>
   <content type="html">&lt;p&gt;Last week I wrote about &lt;a href=&quot;http://blog.florian-hopf.de/2013/11/reindexing-content-in-elasticsearch.html&quot;&gt;reindexing content in Elasticsearch&lt;/a&gt; using a script that extracts the source field from the indexed content. You can use it for cases when your mapping changes or you need to adjust the index settings. After publishing the post &lt;a href=&quot;https://twitter.com/drewr&quot;&gt;Drew Raines&lt;/a&gt; mentioned that there is an easier way using the &lt;a href=&quot;https://github.com/elasticsearch/stream2es&quot;&gt;stream2es&lt;/a&gt; utility only. Time to have a look at it!&lt;/p&gt; &lt;p&gt;stream2es can be used to stream content from several inputs to Elasticsearch. In my last post I used it to stream a file containing the sources of documents to an Elasticsearch index. Besides that it can index data from Wikipedia or Twitter or from Elasticsearch directly, which we will look at now.&lt;/p&gt; &lt;p&gt;Again, we are indexing some documents:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/twitter/tweet/&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;user&quot; : &quot;kimchy&quot;,&lt;br /&gt;    &quot;post_date&quot; : &quot;2009-11-15T14:12:12&quot;,&lt;br /&gt;    &quot;message&quot; : &quot;trying out Elastic Search&quot;&lt;br /&gt;}'&lt;br /&gt;curl -XPOST &quot;http://localhost:9200/twitter/tweet/&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;user&quot; : &quot;kimchy&quot;,&lt;br /&gt;    &quot;post_date&quot; : &quot;2009-11-15T14:14:14&quot;,&lt;br /&gt;    &quot;message&quot; : &quot;Elasticsearch works!&quot;&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now, if we need to adjust the mapping we can just create a new index with the new mapping:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/twitter2&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;mappings&quot; : {&lt;br /&gt;        &quot;tweet&quot; : {&lt;br /&gt;            &quot;properties&quot; : {&lt;br /&gt;                &quot;user&quot; : { &quot;type&quot; : &quot;string&quot;, &quot;index&quot; : &quot;not_analyzed&quot; }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can now use stream2es to transfer the documents from the old index to the new one:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;stream2es es --source http://localhost:9200/twitter/ --target http://localhost:9200/twitter2/&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This will make our documents available in the new index:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET http://localhost:9200/twitter2/_count?pretty=true&lt;br /&gt;{                                                                  &lt;br /&gt;  &quot;count&quot; : 2,                                                      &lt;br /&gt;  &quot;_shards&quot; : {                                                    &lt;br /&gt;    &quot;total&quot; : 5,                                                    &lt;br /&gt;    &quot;successful&quot; : 5,                                               &lt;br /&gt;    &quot;failed&quot; : 0                                                   &lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can now delete the old index. To keep your data available on the same old index name you can also create an alias that will point to your new index:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XDELETE http://localhost:9200/twitter&lt;br /&gt;curl -XPOST 'http://localhost:9200/_aliases' -d '&lt;br /&gt;{&lt;br /&gt;    &quot;actions&quot; : [&lt;br /&gt;        { &quot;add&quot; : { &quot;index&quot; : &quot;twitter2&quot;, &quot;alias&quot; : &quot;twitter&quot; } }&lt;br /&gt;    ]&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Looking at the mapping you can see that the twitter index now points to our updated version:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET http://localhost:9200/twitter/tweet/_mapping?pretty=true&lt;br /&gt;{&lt;br /&gt;  &quot;tweet&quot; : {&lt;br /&gt;    &quot;properties&quot; : {&lt;br /&gt;      &quot;bytes&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;long&quot;&lt;br /&gt;      },&lt;br /&gt;      &quot;message&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;string&quot;&lt;br /&gt;      },&lt;br /&gt;      &quot;offset&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;long&quot;&lt;br /&gt;      },&lt;br /&gt;      &quot;post_date&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;date&quot;,&lt;br /&gt;        &quot;format&quot; : &quot;dateOptionalTime&quot;&lt;br /&gt;      },&lt;br /&gt;      &quot;user&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;string&quot;,&lt;br /&gt;        &quot;index&quot; : &quot;not_analyzed&quot;,&lt;br /&gt;        &quot;omit_norms&quot; : true,&lt;br /&gt;        &quot;index_options&quot; : &quot;docs&quot;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;</content>
 </entry>
 
 <entry>
   <title>Reindexing Content in Elasticsearch</title>
   <link href="http://blog.florian-hopf.de/2013/11/reindexing-content-in-elasticsearch.html"/>
   <updated>2013-11-21T20:32:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/11/reindexing-content-in-elasticsearch</id>
   <content type="html">&lt;p&gt;One of the crucial parts on any search application is the way you map your content to the analyzers. It will determine which query terms match the terms that are indexed with the documents. Sometimes during development you might notice that you didn't get this right from the beginning and need to reindex your data with a new mapping. While for some applications you can easily start the indexing process again this become more difficult for others. Luckily Elasticsearch by default stores the original content in the _source field. In this short article I will show you how to use a script developed by Simon Willnauer that lets you retrieve all the data and reindex it with a new mapping.&lt;/p&gt; &lt;p&gt;&lt;i&gt;You can do the same thing in an easier way using the utility stream2es only. Look at &lt;a href=&quot;http://blog.florian-hopf.de/2013/11/reindexing-content-in-elasticsearch_27.html&quot;&gt;this post&lt;/a&gt; if you are interested&lt;/i&gt;&lt;/p&gt; &lt;h4&gt;Reindexing&lt;/h4&gt; &lt;p&gt;Suppose you have indexed documents in Elasticsearch. Imagine that those are a lot that can not be reindexed again easily or reindexing would take some time.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/twitter/tweet/&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;user&quot; : &quot;kimchy&quot;,&lt;br /&gt;    &quot;post_date&quot; : &quot;2009-11-15T14:12:12&quot;,&lt;br /&gt;    &quot;message&quot; : &quot;trying out Elastic Search&quot;&lt;br /&gt;}'&lt;br /&gt;curl -XPOST &quot;http://localhost:9200/twitter/tweet/&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;user&quot; : &quot;kimchy&quot;,&lt;br /&gt;    &quot;post_date&quot; : &quot;2009-11-15T14:14:14&quot;,&lt;br /&gt;    &quot;message&quot; : &quot;Elasticsearch works!&quot;&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Initially this will create the mapping that is determined from the values.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/twitter/tweet/_mapping?pretty=true&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;tweet&quot; : {&lt;br /&gt;    &quot;properties&quot; : {&lt;br /&gt;      &quot;message&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;string&quot;&lt;br /&gt;      },&lt;br /&gt;      &quot;post_date&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;date&quot;,&lt;br /&gt;        &quot;format&quot; : &quot;dateOptionalTime&quot;&lt;br /&gt;      },&lt;br /&gt;      &quot;user&quot; : {&lt;br /&gt;        &quot;type&quot; : &quot;string&quot;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now if you notice that you would like to change some of the existing fields to another type you need to reindex as Elasticsearch doesn't allow you to modify the mapping for existing fields. Additional fields are fine, but not existing fields. You can leverage the _source field that you can also see when querying a document.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/twitter/tweet/_search?q=user:kimchy&amp;pretty=true&amp;size=1&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;took&quot; : 3,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 2,&lt;br /&gt;    &quot;max_score&quot; : 0.30685282,&lt;br /&gt;    &quot;hits&quot; : [ {&lt;br /&gt;      &quot;_index&quot; : &quot;twitter&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;tweet&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;oaFqxMnqSrex6T7_Ut-erw&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.30685282, &quot;_source&quot; : {&lt;br /&gt;    &quot;user&quot; : &quot;kimchy&quot;,&lt;br /&gt;    &quot;post_date&quot; : &quot;2009-11-15T14:12:12&quot;,&lt;br /&gt;    &quot;message&quot; : &quot;trying out Elastic Search&quot;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;For his &lt;a href=&quot;http://vimeo.com/66303050&quot;&gt;&quot;no slides no bullshit introduction to Elasticsearch&quot;&lt;/a&gt; Simon Willnauer has implemented a &lt;a href=&quot;https://github.com/s1monw/hammertime/master/bin/fetchSource.sh&quot;&gt;script&lt;/a&gt; that retrieves the _source fields for all documents of an index. After installing the prerequisites you can use it by passing in your index name:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;fetchSource.sh twitter &gt; result.json&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;It prints all the documents to stdout which can be redirected to a file. We can now delete our index and recreate it using a different mapping.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XDELETE http://localhost:9200/twitter&lt;br /&gt;curl -XPOST &quot;http://localhost:9200/twitter&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;mappings&quot; : {&lt;br /&gt;        &quot;tweet&quot; : {&lt;br /&gt;            &quot;properties&quot; : {&lt;br /&gt;                &quot;user&quot; : { &quot;type&quot; : &quot;string&quot;, &quot;index&quot; : &quot;not_analyzed&quot; }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The file we just created can now be send to Elasticsearch again using the handy &lt;a href=&quot;https://github.com/elasticsearch/stream2es&quot;&gt;stream2es utility&lt;/a&gt;.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;stream2es stdin --target &quot;http://localhost:9200/twitter/tweet&quot; &lt; result.json&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;All your documents are now indexed using the new mapping.&lt;/p&gt; &lt;h4&gt;Implementation&lt;/h4&gt; &lt;p&gt;Let's look at the details of the script. At the time of writing this post the relevant part of the script looks like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;SCROLL_ID=`curl -s -XGET 'localhost:9200/'${INDEX_NAME}'/_search?search_type=scan&amp;scroll=11m&amp;size=250' -d '{&quot;query&quot; : {&quot;match_all&quot; : {} }}' | jq '._scroll_id' | sed s/\&quot;//g`&lt;br /&gt;RESULT=`curl -s -XGET 'localhost:9200/_search/scroll?scroll=10m' -d ${SCROLL_ID}`&lt;br /&gt;&lt;br /&gt;while [[ `echo ${RESULT} | jq -c '.hits.hits | length'` -gt 0 ]] ; do&lt;br /&gt;  #echo &quot;Processed batch of &quot; `echo ${RESULT} | jq -c '.hits.hits | length'`&lt;br /&gt;  SCROLL_ID=`echo $RESULT | jq '._scroll_id' | sed s/\&quot;//g`&lt;br /&gt;  echo $RESULT | jq -c '.hits.hits[] | ._source + {_id}' &lt;br /&gt;  RESULT=$(eval &quot;curl -s -XGET 'localhost:9200/_search/scroll?scroll=10m' -d ${SCROLL_ID}&quot;)&lt;br /&gt;done&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;It uses &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-scroll.html&quot;&gt;scrolling&lt;/a&gt; to efficiently traverse the documents. Processing of the JSON output is done using &lt;a href=&quot;http://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt;, a lightweight and flexible command-line JSON processor, that I should have used as well when &lt;a href=&quot;http://blog.florian-hopf.de/2013/10/switch-off-legacy-code-violations-in.html&quot;&gt;querying the SonarQube REST API&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;The first line in the script creates a &lt;a href=&quot;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-search-type.html#scan&quot;&gt;scan&lt;/a&gt; search that uses scrolling. The scroll will be valid for 11 minutes, returns 250 documents on each request and queries all documents, as requested with the match_all query. This response doesn't contain any documents but the _scroll_id which is then extracted with jq. The final sed command removes the quotes around it.&lt;/p&gt; &lt;p&gt;The scroll id now is used to send queries to Elasticsearch. On each iteration it is checked if there are any hits at all. If there are the request will return a new scroll id for the next batch. The result is echoed to the console. .hits.hits[] will return the list of all hits. Using the pipe symbol in jq processes each hit with the filter on the right that prints the source as well as the id of the hit.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;The script is a very useful addition to your Elasticsearch toolbox. You can use it to reindex or just export your content. I am glad I looked at the details of the implementation as in the future jq can come in really handy as well.&lt;/p&gt;  </content>
 </entry>
 
 <entry>
   <title>Devoxx in Tweets</title>
   <link href="http://blog.florian-hopf.de/2013/11/devoxx-in-tweets.html"/>
   <updated>2013-11-16T18:51:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/11/devoxx-in-tweets</id>
   <content type="html">&lt;p&gt;For the first time in several years I unfortunately had to skip this years &lt;a href=&quot;http://devoxx.com&quot;&gt;Devoxx&lt;/a&gt;. There are so many tweets that remind me of the good talks going on there and I thought I would do something useful with them. So again I &lt;a href=&quot;http://blog.florian-hopf.de/2013/09/simple-event-analytics-with.html&quot;&gt;indexed them in Elasticsearch using the Twitter river&lt;/a&gt; and therefore can &lt;a href=&quot;http://blog.florian-hopf.de/2013/09/kibana-and-elasticsearch-see-what.html&quot;&gt;look at them using Kibana&lt;/a&gt;. &lt;a href=&quot;https://twitter.com/dadoonet&quot;&gt;David Pilato&lt;/a&gt; also has set up a &lt;a href=&quot;http://es-twitter.dyndns.org/_plugin/kibana/#/dashboard/elasticsearch/Devoxx%202013&quot;&gt;public instance&lt;/a&gt; and I could imagine that there will be a more thorough analysis done by the Devoxx team but here are my thoughts on this years Devoxx without having been there.&lt;/p&gt; &lt;p&gt;I'll be looking at three things: The top 10 mentions, the top 10 hashtags and the tweet distribution over time. For the mentions I have excluded @Devoxx and @java, for the hashtags I have excluded #devoxx, #dvx13 and #dv13 as the mentions and tags are too dominant and don't tell a lot. I have collected all tweets mentioning the term devoxx so there will be a lot I missed. Each retweet counts as a seperate tweet.&lt;/p&gt; &lt;h4&gt;Overall Trends&lt;/h4&gt; &lt;p&gt;Looking at the timeline of the whole week you can see that the amount of tweets is high at the beginning and continually rises with thursday having even more tweets than wednesday which is quite a surprise to me. I would have thought that the first conference day is the one that has the most tweets.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-thKbE5oayE0/UooQhsf-prI/AAAAAAAAANs/fspQF-UgevM/s1600/overall2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-thKbE5oayE0/UooQhsf-prI/AAAAAAAAANs/fspQF-UgevM/s640/overall2.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Stephan007, the founder of Devoxx, has the most mentions which is no surprise. Chet Haase and Romain Guy are following. I have never seen a talk done by them but I probably should. The Dart language is the dominant hashtag with a lot of buzz around their 1.0 release. Java, Android and Scala are still hot technologies. Android is a bit of a surprise here.  It's nice that the initiative &lt;a href=&quot;http://www.devoxx.com/display/4KIDS/Home&quot;&gt;Devoxx4Kids&lt;/a&gt; ranks quite high.&lt;/p&gt; &lt;h4&gt;Daily Analysis&lt;/h4&gt; &lt;h5&gt;Monday&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-BdeNPgxmmhU/UodKqlgZ2qI/AAAAAAAAALw/cKJENJT1X8Y/s1600/monday.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-BdeNPgxmmhU/UodKqlgZ2qI/AAAAAAAAALw/cKJENJT1X8Y/s640/monday.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;On monday the top mention is @AngularJS. Of course this is caused by the two &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; university sessions that lasted nearly the whole day. Angular is a hot topic but I am not yet planning to do any work with it. The session on JavaEE 7 also created a lot of interest as can be seen by the mentions of its hosts Arun Gupta and Antonio Goncalves. They especially encouraged people to participate on Twitter which seems to have been received very well. &lt;a href=&quot;http://www.scala-lang.org/&quot;&gt;Scala&lt;/a&gt; is another hot topic with the university session by Dick Wall and Joshua Suereth that I would really have liked to see.&lt;/p&gt; &lt;h5&gt;Tuesday&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-mvkSLiarL2s/UodK1LWvdAI/AAAAAAAAAL4/xz70pk4cFok/s1600/tuesday.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-mvkSLiarL2s/UodK1LWvdAI/AAAAAAAAAL4/xz70pk4cFok/s640/tuesday.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Tuesday is dominated by the two excellent speakers Matt Raible and Venkat Subramaniam. I especially regret that I couldn't see Venkat in action who I consider to be one of the best speakers available. I am not sure what the tag hackergarden is referring to as I didn't find an event on monday evening or tuesday. There is also quite some interest in &lt;a href=&quot;https://github.com/reactor/reactor&quot;&gt;Reactor&lt;/a&gt;, the reactive framework of the Spring ecosystem.&lt;/p&gt; &lt;h5&gt;Wednesday&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-Lsa0xP7ZCbA/UodLCXuZuRI/AAAAAAAAAMA/HU3Cd8wFYqU/s1600/wednesday.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-Lsa0xP7ZCbA/UodLCXuZuRI/AAAAAAAAAMA/HU3Cd8wFYqU/s640/wednesday.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Brian Goetz got a lot of mentions for the keynote. I think it's a surprise that there are so many mentions of David Blevins for his talk EJB 3.2 and beyond which I wouldn't have expected to be that popular. The big event of the day was the launch of &lt;a href=&quot;http://ceylon-lang.org/blog/2013/11/12/ceylon-1/&quot;&gt;Ceylon 1.0&lt;/a&gt; as can be seen from the hashtag. I heard good things about Ceylon but I still consider it an underdog of the alternative JVM languages.&lt;/p&gt; &lt;h5&gt;Thursday&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-j_62CEeoWHE/UodLLW5CBhI/AAAAAAAAAMI/VRsM7ksxPOw/s1600/thursday.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-j_62CEeoWHE/UodLLW5CBhI/AAAAAAAAAMI/VRsM7ksxPOw/s640/thursday.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Romain Guy is leading the mentions with his very popular talk &quot;Filthy Rich Android Clients&quot;, followed by Jonas Boner of &lt;a href=&quot;http://akka.io&quot;&gt;Akka&lt;/a&gt; fame and Venkat Subramaniam. The launch of &lt;a href=&quot;https://www.dartlang.org/&quot;&gt;Dart 1.0&lt;/a&gt; dominates the keywords. The &lt;a href=&quot;http://javaposse.com/&quot;&gt;Javaposse&lt;/a&gt; still ranks in the top 10 with their popular traditional session.&lt;/p&gt; &lt;h5&gt;Friday&lt;/h5&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-HVGD7B8rkNQ/UodMkzKk9yI/AAAAAAAAAMk/MihkF7I-yzI/s1600/friday.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-HVGD7B8rkNQ/UodMkzKk9yI/AAAAAAAAAMk/MihkF7I-yzI/s640/friday.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Friday normally has fewer participants than the other days. Joshua Suereth received a lot of tweets for his Scala talk, ranking high both in mentions and hashtags. The session on &lt;a href=&quot;http://www.google.com/glass/start/&quot;&gt;Google Glass&lt;/a&gt; also was very popular. I am not sure which session caused the mention of &lt;a href=&quot;https://github.com/square/dagger&quot;&gt;dagger&lt;/a&gt;.&lt;/p&gt; &lt;h4 id=&quot;pl-pop&quot;&gt;Programming Language Popularity&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://nava.de/&quot;&gt;Niko Schmuck&lt;/a&gt; proposed to add the language popularity over the week. As this is quite interesting here is the totally unscientific popularity chart that of course should determine which language you are learning next. I am not querying the hashtags but any mention of the terms.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-hbql6cu1FRY/UoiagrSvDoI/AAAAAAAAANc/oZ4PEvAVwQ0/s1600/language-popularity3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-hbql6cu1FRY/UoiagrSvDoI/AAAAAAAAANc/oZ4PEvAVwQ0/s640/language-popularity3.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Java dominates but JavaScript is very strong on Monday and Thursday. Ceylon has its share on Wednesday while Thursday is the Dart day. Scala is very popular on Monday and Friday.&lt;/p&gt; &lt;p&gt;A ranked version:&lt;/p&gt; &lt;table&gt;&lt;tr&gt;&lt;td&gt;Java&lt;/td&gt;&lt;td&gt;1234&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JS&lt;/td&gt;&lt;td&gt;584&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dart&lt;/td&gt;&lt;td&gt;490&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Scala&lt;/td&gt;&lt;td&gt;252&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ceylon&lt;/td&gt;&lt;td&gt;172&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Groovy&lt;/td&gt;&lt;td&gt;171&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Clojure&lt;/td&gt;&lt;td&gt;94&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Kotlin&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;&lt;/p&gt; &lt;h4&gt;The Drink Tweets&lt;/h4&gt; &lt;p&gt;As there are quite some people tweeting we can see some trends with regards to the drink tweets. First the coffee tweets:&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-Le1xbWDhUpM/UodMqs9KvRI/AAAAAAAAAMs/a9t0COL1zt4/s1600/coffee.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-Le1xbWDhUpM/UodMqs9KvRI/AAAAAAAAAMs/a9t0COL1zt4/s640/coffee.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Quite a spike on Monday with people either mentioning that they need coffee or complaining about the coffee. This repeats on tuesday on wednesday in the morning, people seem to have accepted the situation on thursday.&lt;/p&gt; &lt;p&gt;Another common topic, especially since the conference is located in Belgium, are the beer tweets.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-jpNC2_UlnOQ/UodMwA4Xz6I/AAAAAAAAAM0/BV1QgCsE5vk/s1600/beer.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-jpNC2_UlnOQ/UodMwA4Xz6I/AAAAAAAAAM0/BV1QgCsE5vk/s640/beer.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Surprise, surprise, people tend to tweet about beer in the evening. I like the huge Javaposse-spike on thursday with a lot of mentions of the beer sponsor Atlassian.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;Though I haven't been there I could get a small glimpse of the trends at this years Devoxx. As soon as the videos are available I will buy the account for this years conference, not only because there are so many interesting talks to see but also because the Devoxx team is doing a fantastic job that I'd like to support in any way I can.&lt;/p&gt; &lt;h6&gt;Updates&lt;/h6&gt; &lt;ul&gt;&lt;li&gt;17.11. Added a section on programming language popularity&lt;/li&gt;&lt;li&gt;18.11. Updated the weekly diagram with a more accurate&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Lucene Solr Revolution 2013 in Dublin</title>
   <link href="http://blog.florian-hopf.de/2013/11/lucene-solr-revolution-2013-in-dublin.html"/>
   <updated>2013-11-10T19:06:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/11/lucene-solr-revolution-2013-in-dublin</id>
   <content type="html">&lt;p&gt;I just returned from &lt;a href=&quot;http://lucenerevolution.org&quot;&gt;Lucene Solr Revolution Europe&lt;/a&gt;, the conference on everything Lucene and Solr which this year was held in Dublin. I always like to recap what I took from a conference so here are some impressions.&lt;/p&gt; &lt;h4&gt;The Venue&lt;/h4&gt; &lt;p&gt;In the spirit of last years conference, which was merged with ApacheCon and held in a soccer stadium in Sinsheim, this years venue was a Rugby Stadium. It's seems to be quite common that conferences are organized there and the location was well suited. For some of the room changes you had to walk quite a distance but that's nothing that couldn't be managed.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-RLA6qdnqU5U/Un9mOXRUS3I/AAAAAAAAALY/Z9_cK8xdlXQ/s1600/2013-11-06+09.04.27.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-RLA6qdnqU5U/Un9mOXRUS3I/AAAAAAAAALY/Z9_cK8xdlXQ/s400/2013-11-06+09.04.27.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;The Talks&lt;/h4&gt; &lt;p&gt;As there were four tracks in parallel choosing the talk to attend could prove to be difficult. There were so many interesting things to choose from. Fortunately all the talks have been recorded and will be made available for free on the conference website.&lt;/p&gt; &lt;p&gt;The following are a selection of talks that I think were most valuable to me.&lt;/p&gt; &lt;h5&gt;Keynote: Michael Busch on Lucene at Twitter&lt;/h5&gt; &lt;p&gt;Michael Busch is a regular speaker at Search conferences because Twitter is doing some interesting things. On the one hand they have to handle near realtime search, massive data sets and lots of requests. On the other hand they can always be sure that their documents are of a certain size. They maintain two different data stores as Lucene indices, the realtime index that contains the most recent data and the archive index that makes older tweets searchable. They introduced the archive index only a few months ago which in my opinion led to a far more reliable search experience. They have done some really interesting things like encoding the position info of a term with the doc id because they only need few bits to address positions in a 140 character document. Also they changed some aspects of the posting list encoding because they always display results sorted by date. They are trying to make their changes more general so those can be contributed back to Lucene.&lt;/p&gt; &lt;h5&gt;Solr Indexing and Analysis Tricks by Erik Hatcher&lt;/h5&gt; &lt;p&gt;I always enjoy listening to the talks of Erik Hatcher, probably also because his &lt;a href=&quot;http://parleys.com/play/514892260364bc17fc56bde5/chapter0/about&quot;&gt;university session at Devoxx 2009&lt;/a&gt; was the driving factor for me starting to use Solr. In this years talk he presented lots of useful aspects for indexing data in Solr. One of the most interesting facts I took from this talk is the use of the &lt;a href=&quot;http://wiki.apache.org/solr/ScriptUpdateProcessor&quot;&gt;ScriptUpdateProcessor&lt;/a&gt; that is included in Solr since version 4. You can define scripts that are executed during indexing and can manipulate the document. This is a valuable alternative to copyFields, especially if you would like to have the content stored as well. By default you can implement the logic in JavaScript but there are alternatives available.&lt;/p&gt; &lt;h5&gt;Hacking Lucene and Solr for Fun and Profit by Grant Ingersoll&lt;/h5&gt; &lt;p&gt;Grant Ingersoll presented some applications of Lucene and Solr not directly involving search like Classification, Recommendations and Analytics. Some examples had been taken from his &lt;a href=&quot;http://www.manning.com/ingersoll/&quot;&gt;excellent book Taming Text&lt;/a&gt; (watch this blog for a review of the book in the near future).&lt;/p&gt; &lt;h5&gt;Schemaless Solr and the Solr Schema REST API by Steve Rowe&lt;/h5&gt; &lt;p&gt;One of the factors of the success of Elasticsearch is its ease of use. You can download it and start indexing documents immediately without doing any configuration work. One of the features that enables you to do this is the autodiscovery of fields by value. Starting with Solr 4.4 you can now use Solr in a similar way. You can configure that you want Solr to manage your schema. This way unknown fields are then created automatically based on the first value that is extracted by configured parsers. As with Elasticsearch you shouldn't rely on this feature exclusively so there is also a way to add new fields of a certain type via the &lt;a href=&quot;http://wiki.apache.org/solr/SchemaRESTAPI&quot;&gt;Schema REST API&lt;/a&gt;. When Solr is in managed mode it will modify the schema.xml so you might lose changes you made manually. For the future the developers are even thinking about moving away from XML for the managed mode as there are better options for when readability doesn't matter.&lt;/p&gt; &lt;h5&gt;Stump the Chump with Chris Hostetter&lt;/h5&gt; &lt;p&gt;This seems to be a tradition at Lucene Solr Revolution. Chris Hostetter has to find solutions to problems that have been submitted before or are posted by the audience. It's a fun event but you can also learn a lot.&lt;/p&gt; &lt;h5&gt;Query Latency Optimization with Lucene by Stefan Pohl&lt;/h5&gt; &lt;p&gt;Stefan first introduced some basic latency factors and how to measure them. He recommended to not instrument the low level Lucene classes when profiling your application as those rely heavily on hotspot optimizations. Besides introducing the basic mechanisms of how conjunction (AND) and disjunction (OR) work he described some recent Lucene improvements that can speed up your application, among those &lt;a href=&quot;https://issues.apache.org/jira/browse/LUCENE-4571&quot;&gt;LUCENE-4571&lt;/a&gt;, the new minShouldMatch implementation and &lt;a href=&quot;https://issues.apache.org/jira/browse/LUCENE-4752&quot;&gt;LUCENE-4752&lt;/a&gt;, which allows custom ordering of documents in the index.&lt;/p&gt; &lt;h5&gt;Relevancy Hacks for eCommerce by Varun Thacker&lt;/h5&gt; &lt;p&gt;Varun introduced the basics of relevancy sorting in Lucene and Solr and how those might affect product searches. TF/IDF is sometimes not the best solution (&quot;IDF is a measurement of rarity not necessarily importance&quot;). He also showed the ways to influence the relevancy: Implementation of a custom Similarity class, boosting and function queries.&lt;/p&gt; &lt;h5&gt;What is in a Lucene Index by Adrien Grand&lt;/h5&gt; &lt;p&gt;Adrien started with the basics fo a Lucene index and how it differs from a database index: the dictionary structure, segments and merging. He then moved on to topics like the structure of the posting list, term vectors, the FST terms index and the difference between stored fields and doc values. This is a talk full of interesting details on the internal workings of Lucene and the implications for the performance of your application.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;As said before I couldn't attend all the talks I would have liked. I especially heard good things about the following talks which I will watch as soon as those are available:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;Integrate Solr with Real-Time Stream Processing Applications by Timothy Potter&lt;/li&gt;&lt;li&gt;The Typed Index by Christoph Goller&lt;/li&gt;&lt;li&gt;Implementing a Custom Search Syntax Using Solr, Lucene and Parboiled by John Berryman&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;I really enjoyed Lucene Solr Revolution. Not only were there a lot of interesting talks to listen to but it was also a good opportunity to meet new people. On both evenings there have been get togethers with free drinks and food which must have cost LucidWorks a fortune. I couldn't attend the closing remarks but I heard they announced that they want to move to smaller, national events in Europe instead of the central conference. I hope those will still be events that attract so many commiters and interesting people.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Switch Off Legacy Code Violations in SonarQube</title>
   <link href="http://blog.florian-hopf.de/2013/10/switch-off-legacy-code-violations-in.html"/>
   <updated>2013-10-31T16:04:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/10/switch-off-legacy-code-violations-in</id>
   <content type="html">&lt;p&gt;While I don't believe in putting numbers on source code quality, &lt;a href=&quot;http://www.sonarqube.org/&quot;&gt;SonarQube&lt;/a&gt; (formerly known as Sonar) can be a really useful tool during development. It enforces a consistent style across your team, has discovered several possible bugs for me and is a great tool to learn: You can browse the violations and see why a certain expression or code block can be a problem.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-UTphslYDi3M/UnILXlQlmYI/AAAAAAAAALA/4kDjnuusQBI/s1600/sonar-violations.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-UTphslYDi3M/UnILXlQlmYI/AAAAAAAAALA/4kDjnuusQBI/s400/sonar-violations.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;To make sure that your code base stays in a consistent state you can also go as far as mandating that there should be no violations in the code developers check in. One of the problems with this is that a lot of projects are not green field projects and you have a lot of existing code. If your violation number already is high it is difficult to judge if no new violations were introduced.&lt;/p&gt;   &lt;p&gt;In this post I will show you how you can start with zero violations for existing code without touching the sources, something I got inspired to do by &lt;a href=&quot;http://www.schauderhaft.de/&quot;&gt;Jens Schauder&lt;/a&gt; in his great talk Working with Legacy Teams. We will ignore all violations based on the line in the file so if anybody touches the file the violations will show again and the developer is responsible for fixing the legacy violations.&lt;/p&gt; &lt;h4&gt;The Switch Off Violations Plugin&lt;/h4&gt; &lt;p&gt;We are using the &lt;a href=&quot;http://docs.codehaus.org/display/SONAR/Switch+Off+Violations+Plugin&quot;&gt;Switch Off Violations Plugin&lt;/a&gt; for SonarQube. It can be configured with different exclusion patterns for the issues. You can define regular expressions for code blocks that should be ignored or deactivate violations at all or on a file or line basis.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-6uRVSzEOed0/UnIMX1j_lHI/AAAAAAAAALI/Gx3XSa4F7yI/s1600/switch-off-violations.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-6uRVSzEOed0/UnIMX1j_lHI/AAAAAAAAALI/Gx3XSa4F7yI/s400/switch-off-violations.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;For existing code you want to ignore all violations for certain files and lines. This can be done by inserting something like this in the text area Exclusion patterns:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;de.fhopf.akka.actor.IndexingActor;pmd:SignatureDeclareThrowsException;[23]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This will exclude the violation for throwing raw Exceptions in line 23 of the IndexingActor class. When analyzing the code again this violation will be ignored.&lt;/p&gt; &lt;h4&gt;Retrieving violations via the API&lt;/h4&gt; &lt;p&gt;Besides the nice dashboard SonarQube also offers an API that can be used to retrieve all the violations for a project. If you are not keen to look up all existing violations in your code base and insert those by hand you can use it to generate the exclusion patterns automatically. All of the violations can be found at /api/violations, e.g. http://localhost:9000/api/violations.&lt;/p&gt; &lt;p&gt;I am sure there are other ways to do it but I used &lt;a href=&quot;https://github.com/micha/jsawk&quot;&gt;jsawk&lt;/a&gt; to parse the JSON response (On Ubuntu you &lt;a href=&quot;https://github.com/micha/jsawk/issues/8&quot;&gt;have to install Spidermonkey instead of the default js interpreter.&lt;/a&gt;. And &lt;a href=&quot;http://stackoverflow.com/questions/6656904/best-way-to-get-spidermonkey-js-on-ubuntu&quot;&gt;you have to compile it yourself.&lt;/a&gt; And I had to &lt;a href=&quot;https://github.com/micha/jsawk/issues/20&quot;&gt;use a specific version&lt;/a&gt;. Sigh.).&lt;/p&gt; &lt;p&gt;Once you have set up all the components you can now use jsawk to create the exclusion patterns for all existing violations:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET 'http://localhost:9000/api/violations?depth=-1' | ./jsawk -a 'return this.join(&quot;\n&quot;)' 'return this.resource.key.split(&quot;:&quot;)[1] + &quot;;*;[&quot; + this.line + &quot;]&quot;' | sort | uniq&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This will present a list that can just be pasted in the text area of the Switch Off Violations plugin or checked in to the repository as a file. With the next analysis process you will then hopefully see zero violations. When somebody changes a file by inserting a line the violations will be shown again and should be fixed. Unfortunately some violations are not line based and will yield a line number 'undefined'. Currently I just removed those manually so you still might see some violations.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;I presented one way to reset your legacy code base to zero violations. With SonarQube 4.0 the functionality of the Switch Violations Off plugin will be available in the core so it will be easier to use. I am still looking for the best way to keep the exclusion patterns up to date. Once somebody had to fix the violations for an existing file the pattern should be removed.&lt;/p&gt; &lt;h4 id=&quot;update&quot;&gt;Update 09.01.2014&lt;/h4&gt; &lt;p&gt;Starting with SonarQube 4 this approach doesn't work anymore. Some features of the SwitchOffViolations plugin have been moved to the core but excluding violations by line is not possible anymore and also will not be implemented. &lt;a href=&quot;http://sonarqube.15.x6.nabble.com/Ignoring-Violations-by-line-file-tp5020749p5020775.html&quot;&gt;The developers recommend&lt;/a&gt; to only look at the trends of the project and not the overall violation count. This can be done nicely using the &lt;a href=&quot;http://www.sonarqube.org/differentials-four-ways-to-see-whats-changed/&quot;&gt;differentials&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Elasticsearch at Scale - Kiln and GitHub</title>
   <link href="http://blog.florian-hopf.de/2013/10/elasticsearch-at-scale-kiln-and-github.html"/>
   <updated>2013-10-25T14:33:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/10/elasticsearch-at-scale-kiln-and-github</id>
   <content type="html">&lt;p&gt;Most of us are not exposed to data at real scale. It is getting more common but still I appreciate that more progressive companies that have to fight with large volumes of data are open about it and talk about their problems and solutions. GitHub and Fog Creek are two of the larger users of Elasticsearch and both have published articles and interviews on their setup. It's interesting that both of these companies are using it for a very specialized use case, source code search. As I have recently read the article on Kiln as well as the interview with the folks at GitHub I'd like to summarize some of the points they made. Visit the original links for in depth information.&lt;/p&gt; &lt;h4&gt;Elasticsearch at Fog Creek for Kiln&lt;/h4&gt; &lt;p&gt;In this &lt;a href=&quot;http://www.infoq.com/articles/kiln-elasticsearch&quot;&gt;article on InfoQ&lt;/a&gt; Kevin Gessnar, a developer at Fog Creek describes the process of migrating the code search of Kiln to Elasticsearch.&lt;/p&gt; &lt;h5&gt;Initial Position&lt;/h5&gt; &lt;p&gt;Kiln allows you to search on commit messages, filenames and file contents. For commit messages and filenames they were initially using the full text search features of SQL Server. For the file content search they were using a tool called &lt;a href=&quot;http://opengrok.github.io/OpenGrok/&quot;&gt;OpenGrok&lt;/a&gt; that leverages &lt;a href=&quot;http://en.wikipedia.org/wiki/Ctags&quot;&gt;Ctags&lt;/a&gt; to analyze the code and stores it in a Lucene index. This provided them will all of the features they needed but unfortunately the solution couldn't scale with their requirements. Queries took several seconds up to the timeout value of 30 seconds.&lt;/p&gt; &lt;p&gt;It's interesting to see that they decided against Solr because of poor read performance on heavy writes. Would be interesting to see if this is still the case for current versions.&lt;/p&gt; &lt;h5&gt;Scale&lt;/h5&gt; &lt;p&gt;They are indexing several million documents every day, which comes to terabytes of data. They are still running their production system on two nodes only. These are numbers that really surprised me. I would have guessed that you need more nodes for this amount of data (well, probably those are really big machines). They only seem to be using Elasticsearch for indexing and search but retrieve the result display data from their primary storage layer.&lt;/p&gt; &lt;h4&gt;Elasticsearch at GitHub&lt;/h4&gt; &lt;p&gt;Andrew Cholakian, who is doing a great job with writing his book &lt;a href=&quot;http://exploringelasticsearch.com/book/&quot;&gt;Exploring Elasticsearch&lt;/a&gt; in the open, published &lt;a href=&quot;http://exploringelasticsearch.com/book/elasticsearch-at-scale-interviews/interview-with-the-github-elasticsearch-team.html&quot;&gt;an interview with Tim Pease and Grant Rodgers of GitHub on their Elasticsearch setup&lt;/a&gt;, going through a lot of details.&lt;/p&gt; &lt;h5&gt;Initial Position&lt;/h5&gt; &lt;p&gt;GitHub used to have their search based on Solr. As the volume of data and search increased they needed a solution that scales. Again, I would be interested if current versions of Solr Cloud could handle this volume.&lt;/p&gt; &lt;h5&gt;Scale&lt;/h5&gt; &lt;p&gt;They are really searching big data. 44 Amazon EC2 instances power search on 2 billion documents which make up 30 terabyte of data. 8 instances don't hold any data but are only there to distribute the queries. They are planning to move from the 44 Amazon instances to 8 larger physical machines. Besides their user facing data they are indexing internal data like audit logs and exceptions (it isn't clear to me from the interview if in this case Elasticsearch is their primary data store which would be remarkable). They are using different clusters for different data types so that the external search is not affected when there are a lot of exceptions.&lt;/p&gt; &lt;h5&gt;Challenges&lt;/h5&gt; &lt;p&gt;Shortly after launching their new search feature people started discovering that you could also search for files people had accidentally commited like private ssh keys or passwords. This is an interesting phenomen where just the possibility for better retrieval made a huge difference. All the information had been there before but it just couldn't be found easily. This led to an increase in search volume that was not anticipated. Due to some configuration issues (suboptimal Java version, no setting for minimum of masters) their cluster became unstable and they had to disable search for the whole site.&lt;/p&gt; &lt;h5&gt;Further Takeaways&lt;/h5&gt; &lt;ul&gt;&lt;li&gt;Use routing to keep your data together on one shard&lt;/li&gt;&lt;li&gt;Thrift seems to be far more complicated from an ops point of view compared to HTTP&lt;/li&gt;&lt;li&gt;Use the slow query log&lt;/li&gt;&lt;li&gt;Time slicing your indices is a good idea if the data allows&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;A Common Theme&lt;/h4&gt; &lt;p&gt;Both of these articles have some observations in common:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;Elasticsearch is easy to get started with&lt;/li&gt;&lt;li&gt;Scaling is not an issue&lt;/li&gt;&lt;li&gt;the HTTP interface is good for debugging and operations&lt;/li&gt;&lt;li&gt;the Elasticsearch community and the company are really helpful when it comes to problems&lt;/li&gt;&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Cope with Failure - Actor Supervision in Akka</title>
   <link href="http://blog.florian-hopf.de/2013/10/cope-with-failure-actor-supervision-in.html"/>
   <updated>2013-10-11T14:04:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/10/cope-with-failure-actor-supervision-in</id>
   <content type="html">&lt;p&gt;A while ago I showed &lt;a href=&quot;http://blog.florian-hopf.de/2012/08/getting-rid-of-synchronized-using-akka.html&quot;&gt;an example on how to use Akka to scale a simple application with multiple threads&lt;/a&gt;. Tasks can be split into several actors that communicate via immutable messages. State is encapsulated and each actor can be scaled independently. While implementing an actor you don't have to take care of low level building blocks like Threads and synchronization so it is far more easy to reason about the application.&lt;/p&gt; &lt;p&gt;Besides these obvious benefits, fault tolerance is another important aspect. In this post I'd like to show you how you can leverage some of Akkas characteristics to make our example more robust.&lt;/p&gt; &lt;h4&gt;The Application&lt;/h4&gt; &lt;p&gt;To recap, we are building a simple web site crawler in Java to index pages in Lucene. The full code of the examples is available on &lt;a href=&quot;https://github.com/fhopf/akka-crawler-example&quot;&gt;GitHub&lt;/a&gt;. We are using three actors: one which carries the information on the pages to be visited and visited already, one that downloads and parses the pages and one that indexes the pages in Lucene.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-I5TC1mL-bHI/UC5hMo0rtkI/AAAAAAAAAE8/6OGAL2hsBzg/s1600/actor-setup.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;214&quot; width=&quot;320&quot; src=&quot;http://2.bp.blogspot.com/-I5TC1mL-bHI/UC5hMo0rtkI/AAAAAAAAAE8/6OGAL2hsBzg/s320/actor-setup.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;By using several actors to download and parse pages we could see some good performance improvements.&lt;/p&gt; &lt;h4&gt;What could possibly go wrong?&lt;/h4&gt; &lt;p&gt;Things will fail. We are relying on external services (the page we are crawling) and therefore the network. Requests could time out or our parser could choke on the input. To make our example somewhat reproducible I just simulated an error. A new PageRetriever, the ChaosMonkeyPageRetriever sometimes just throws an Exception:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;@Override&lt;br /&gt;public PageContent fetchPageContent(String url) {&lt;br /&gt;    // this error rate is derived from scientific measurements&lt;br /&gt;    if (System.currentTimeMillis() % 20 == 0) {&lt;br /&gt;      throw new RetrievalException(&quot;Something went horribly wrong when fetching the page.&quot;);&lt;br /&gt;    }&lt;br /&gt;    return super.fetchPageContent(url);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can surely imagine what happens when we use this retriever in the sequential example that doesn't use Akka or threads. As we didn't take care of the failure our application just stops when the Exception occurs. One way we could mitigate this is by surrounding statements with try/catch-Blocks but this will soon intermingle a lot of recovery and fault processing code with our application logic. Once we have an application that is running in multiple threads fault processing gets a lot harder. There is no easy way to notify other Threads or save the state of the failing thread.&lt;/p&gt; &lt;h4&gt;Supervision&lt;/h4&gt; &lt;p&gt;Let's see Akkas behavior in case of an error. I added some logging that indicates the current state of the visited pages.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;1939 [default-akka.actor.default-dispatcher-5] INFO de.fhopf.akka.actor.Master - inProgress:  55, allPages:  60&lt;br /&gt;1952 [default-akka.actor.default-dispatcher-4] INFO de.fhopf.akka.actor.Master - inProgress:  54, allPages:  60&lt;br /&gt;[ERROR] [10/10/2013 06:47:39.752] [default-akka.actor.default-dispatcher-5] [akka://default/user/$a/$a] Something went horribly wrong when fetching the page.&lt;br /&gt;de.fhopf.akka.RetrievalException: Something went horribly wrong when fetching the page.&lt;br /&gt;        at de.fhopf.akka.actor.parallel.ChaosMonkeyPageRetriever.fetchPageContent(ChaosMonkeyPageRetriever.java:21)&lt;br /&gt;        at de.fhopf.akka.actor.PageParsingActor.onReceive(PageParsingActor.java:26)&lt;br /&gt;        at akka.actor.UntypedActor$$anonfun$receive$1.applyOrElse(UntypedActor.scala:167)&lt;br /&gt;        at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)&lt;br /&gt;        at akka.actor.ActorCell.invoke(ActorCell.scala:456)&lt;br /&gt;        at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)&lt;br /&gt;        at akka.dispatch.Mailbox.run(Mailbox.scala:219)&lt;br /&gt;        at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:386)&lt;br /&gt;        at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)&lt;br /&gt;        at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)&lt;br /&gt;        at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)&lt;br /&gt;        at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)&lt;br /&gt;&lt;br /&gt;1998 [default-akka.actor.default-dispatcher-8] INFO de.fhopf.akka.actor.Master - inProgress:  53, allPages:  60&lt;br /&gt;2001 [default-akka.actor.default-dispatcher-12] INFO de.fhopf.akka.actor.PageParsingActor - Restarting PageParsingActor because of class de.fhopf.akka.RetrievalException&lt;br /&gt;2001 [default-akka.actor.default-dispatcher-2] INFO de.fhopf.akka.actor.PageParsingActor - Restarting PageParsingActor because of class de.fhopf.akka.RetrievalException&lt;br /&gt;2001 [default-akka.actor.default-dispatcher-10] INFO de.fhopf.akka.actor.PageParsingActor - Restarting PageParsingActor because of class de.fhopf.akka.RetrievalException&lt;br /&gt;[...]&lt;br /&gt;2469 [default-akka.actor.default-dispatcher-12] INFO de.fhopf.akka.actor.Master - inProgress:   8, allPages:  78&lt;br /&gt;2487 [default-akka.actor.default-dispatcher-7] INFO de.fhopf.akka.actor.Master - inProgress:   7, allPages:  78&lt;br /&gt;2497 [default-akka.actor.default-dispatcher-5] INFO de.fhopf.akka.actor.Master - inProgress:   6, allPages:  78&lt;br /&gt;2540 [default-akka.actor.default-dispatcher-13] INFO de.fhopf.akka.actor.Master - inProgress:   5, allPages:  78&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We can see each exception that is happening in the log file but our application keeps running. That is because of &lt;a href=&quot;http://doc.akka.io/docs/akka/snapshot/general/supervision.html&quot;&gt;Akkas supervision support&lt;/a&gt;. Actors form hierarchies where our PageParsingActor is a child of the Master actor because it is created from its context. The Master is responsible to determine the fault strategy for its children. By default it will restart the Actor in case of an exception which makes sure that the next message is processed correctly. This means even in case of an error Akka tries to keep the system in a running state.&lt;/p&gt;     &lt;p&gt;The reaction to a failure is determined by the method supervisorStrategy() in the parent actor. Based on an Exception class you can choose several outcomes:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;resume: Keep the actor running as if nothing had happened&lt;/li&gt;&lt;li&gt;restart: Replace the failing actor with a new instance&lt;/li&gt;&lt;li&gt;suspend: Stop the failing actor&lt;/li&gt;&lt;li&gt;escalate: Let your own parent decide on what to do&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;A supervisor that would restart the actor for our exception and escalate otherwise could be added like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;// allow 100 restarts in 1 minute ... this is a lot but we the chaos monkey is rather busy&lt;br /&gt;private SupervisorStrategy supervisorStrategy = new OneForOneStrategy(100, Duration.create(&quot;1 minute&quot;), new Function&lt;Throwable, Directive&gt;() {&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public Directive apply(Throwable t) throws Exception {&lt;br /&gt;        if (t instanceof RetrievalException) {&lt;br /&gt;            return SupervisorStrategy.restart();&lt;br /&gt;        }&lt;br /&gt;        // it would be best to model the default behaviour in other cases&lt;br /&gt;        return SupervisorStrategy.escalate();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;@Override&lt;br /&gt;public SupervisorStrategy supervisorStrategy() {&lt;br /&gt;    return supervisorStrategy;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Let's come back to our example. Though Akka takes care of restarting our failing actors the end result doesn't look good. The application continues to run after several exceptions but our application then just stops and hangs. This is caused by our business logic. The Master actor keeps all pages to visit in the VisitedPageStore and only commits the Lucene index when all pages are visited. As we had several failures we didn't receive the result for those pages and the Master still waits.&lt;/p&gt; &lt;p&gt;One way to fix this is to resend the message once the actor is restarted. Each Actor class can implement some methods that hook into the actors lifecycle. In preRestart() we can just send the message again.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;@Override&lt;br /&gt;public void preRestart(Throwable reason, Option&amp;lt;Object&amp;gt; message) throws Exception {&lt;br /&gt;    logger.info(&quot;Restarting PageParsingActor and resending message '{}'&quot;, message);&lt;br /&gt;    if (message.nonEmpty()) {&lt;br /&gt;        getSelf().forward(message.get(), getContext());&lt;br /&gt;    }&lt;br /&gt;    super.preRestart(reason, message);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Now if we run this example we can see our actors recover from the failure. Though some exceptions are happening all pages get visited eventually and everything will be indexed and commited in Lucene.&lt;/p&gt; &lt;p&gt;Though resending seems to be the solution to our failures you need to be careful to not break your system with it: For some applications the message might be the cause for the failure and by resending it you will keep your system busy with it in a livelock state. When using this approach you should at least add a count to the message that you can increment on restart. Once it is sent too often you can then escalate the failure to have it handled in a different way.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;We have only handled one certain type of failure but you can already see how powerful Akka can be when it comes to fault tolerance. Recovery code is completely separated from the business code. To learn more on different aspects of error handling read the Akka documentation on &lt;a href=&quot;http://doc.akka.io/docs/akka/snapshot/general/supervision.html&quot;&gt;supervision&lt;/a&gt; and &lt;a href=&quot;http://doc.akka.io/docs/akka/snapshot/java/fault-tolerance.html&quot;&gt;fault tolerance&lt;/a&gt; or &lt;a href=&quot;http://danielwestheide.com/blog/2013/03/20/the-neophytes-guide-to-scala-part-15-dealing-with-failure-in-actor-systems.html&quot;&gt;this excellent article by Daniel Westheide&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Brian Foote on Prototyping</title>
   <link href="http://blog.florian-hopf.de/2013/10/brian-foote-on-prototyping.html"/>
   <updated>2013-10-04T15:09:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/10/brian-foote-on-prototyping</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.laputan.org/mud/mud.html&quot;&gt;Big Ball of Mud&lt;/a&gt; is a collection of patterns by &lt;a href=&quot;http://www.laputan.org/&quot;&gt;Brian Foote&lt;/a&gt;, published in 1999. The title stems from one of the patterns, Big Ball of Mud, the &quot;most frequently deployed of software architectures&quot;. Though this might sound like a joke at first the article contains a lot of really useful information on the forces at work when dealing with large codebases and legacy code. I especially like his take on &lt;a href=&quot;http://www.laputan.org/mud/mud.html#ThrowAwayCode&quot;&gt;prototyping applications&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Did you ever find yourself in the following situation? A customer agrees to build a prototype of an application to learn and to see something in action. After the prototype is finished the customer tries to force you to reuse the prototype, &quot;because it already does what we need&quot;. Cold sweat, you probably were taking lots of shortcuts in the prototype and didn't build it with maintainability in mind. This is what Brian recommends to circumvent this situation:&lt;/p&gt; &lt;blockquote&gt;One way to minimize the risk of a prototype being put into production is to write the prototype in using a language or tool that you couldn't possibly use for a production version of your product.&lt;/blockquote&gt; &lt;p&gt;Three observations:&lt;/p&gt; &lt;ol&gt;&lt;li&gt;Nowadays the choice of languages doesn't matter that much for running the code in production with virtual machines that support a lot of languages.&lt;/li&gt;&lt;li&gt;This only holds true for prototypes that are used to explore the domain. When doing a technical proof of concept at least some parts of the prototype need to use the intended technology.&lt;/li&gt;&lt;li&gt;Prototypes are sometimes also used to make the team familiar with a new technology that is set for the project.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Nevertheless this is a really useful advice to keep in mind.&lt;/p&gt;   </content>
 </entry>
 
 <entry>
   <title>Feature Toggles in JSP with Togglz</title>
   <link href="http://blog.florian-hopf.de/2013/09/feature-toggles-in-jsp-with-togglz.html"/>
   <updated>2013-09-27T16:30:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/09/feature-toggles-in-jsp-with-togglz</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://martinfowler.com/bliki/FeatureToggle.html&quot;&gt;Feature Toggles&lt;/a&gt; are a useful pattern when you are working on several features but want to keep your application in a deployable state. One of the implementations of the pattern available for Java is &lt;a href=&quot;http://togglz.org&quot;&gt;Togglz&lt;/a&gt;. It provides ways to check if a feature is enabled programmatically, from JSF or JSP pages or even when wiring Spring beans. I couldn't find a single example on how to use the JSP support so I created an &lt;a href=&quot;https://github.com/fhopf/togglz-jsp-example&quot;&gt;example project and pushed it to GitHub&lt;/a&gt;. In this post I will show you the basics of Togglz and how to use it in Java Server Pages.&lt;/p&gt; &lt;h4&gt;Togglz&lt;/h4&gt; &lt;p&gt;Features that you want to make configurable are described with a Java Enum. This is an example with two features that can be enabled or disabled:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public enum ToggledFeature implements Feature {&lt;br /&gt;&lt;br /&gt;    TEXT,&lt;br /&gt;    MORE_TEXT;&lt;br /&gt;&lt;br /&gt;    public boolean isActive() {&lt;br /&gt;        return FeatureContext.getFeatureManager().isActive(this);&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This Enum can then be used to check if a feature is enabled in any part of your code:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;if (ToggledFeature.TEXT.isActive()) {&lt;br /&gt;    // do something clever&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The config class is used to wire the feature enum with a configuration mechanism:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public class ToggledFeatureConfiguration implements TogglzConfig {&lt;br /&gt;&lt;br /&gt;    public Class&amp;lt;? extends Feature&amp;gt; getFeatureClass() {&lt;br /&gt;        return ToggledFeature.class;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public StateRepository getStateRepository() {&lt;br /&gt;        return new FileBasedStateRepository(new File(&amp;quot;/tmp/features.properties&amp;quot;));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public UserProvider getUserProvider() {&lt;br /&gt;        return new ServletUserProvider(&amp;quot;ADMIN_ROLE&amp;quot;);&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The StateRepository is used for enabling and disabling features. We are using a file based one but there are others available.&lt;/p&gt; &lt;p&gt;To configure Togglz for your webapp you can either do it using CDI, Spring or via manual configuration in the web.xml:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;web-app xmlns=&amp;quot;http://java.sun.com/xml/ns/javaee&amp;quot;&lt;br /&gt;    xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;    xsi:schemaLocation=&amp;quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&amp;quot;&lt;br /&gt;    version=&amp;quot;3.0&amp;quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;context-param&amp;gt;&lt;br /&gt;        &amp;lt;param-name&amp;gt;org.togglz.core.manager.TogglzConfig&amp;lt;/param-name&amp;gt;&lt;br /&gt;        &amp;lt;param-value&amp;gt;de.fhopf.togglz.ToggledFeatureConfiguration&amp;lt;/param-value&amp;gt;&lt;br /&gt;    &amp;lt;/context-param&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;filter&amp;gt;&lt;br /&gt;        &amp;lt;filter-name&amp;gt;TogglzFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;        &amp;lt;filter-class&amp;gt;org.togglz.servlet.TogglzFilter&amp;lt;/filter-class&amp;gt;&lt;br /&gt;    &amp;lt;/filter&amp;gt;&lt;br /&gt;    &amp;lt;filter-mapping&amp;gt;&lt;br /&gt;        &amp;lt;filter-name&amp;gt;TogglzFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;        &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;    &amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/web-app&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;In my example I had to add the filter manually though with Servlet 3.0 this shouldn't be necessary. I am not sure if this is caused by the way Gradle runs Jetty or if this is always the case when doing the configuration via a context-param.&lt;/p&gt; &lt;h4&gt;Togglz with Java Server Pages&lt;/h4&gt; &lt;p&gt;For the integration of Togglz in JSPs you need to add the dependency togglz-jsp to your project. It contains a tag that can be used to group code which can then be enabled or disabled. A simple example for our ToggledFeature:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;%@ taglib uri=&amp;quot;http://togglz.org/taglib&amp;quot; prefix=&amp;quot;togglz&amp;quot; %&amp;gt;&lt;br /&gt;&lt;br /&gt;This is some text that is always shown.&lt;br /&gt;&lt;br /&gt;&amp;lt;togglz:feature name=&amp;quot;TEXT&amp;quot;&amp;gt;&lt;br /&gt;This is the text of the TEXT feature.&lt;br /&gt;&amp;lt;/togglz:feature&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;togglz:feature name=&amp;quot;MORE_TEXT&amp;quot;&amp;gt;&lt;br /&gt;This is the text of the MORE_TEXT feature.&lt;br /&gt;&amp;lt;/togglz:feature&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Both features will be disabled by default so you will only see the first sentence. You can control which features are enabled (even at runtime) in /tmp/features.properties. This is what it looks like when the TEXT feature is enabled:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;TEXT=true&lt;br /&gt;MORE_TEXT=false&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;A Word of Caution&lt;/h4&gt; &lt;p&gt;I am just starting using feature toggles in an application so I wouldn't call me experienced. But I have the impression that you need to be really disciplined when using it. Old feature toggles that are not used should be removed as soon as possible. Unfortunately the huge benefit of compile time safety in Java for removing a feature from the enum is gone with JSPs; the names of the features are only Strings so you will have to do some file searches when removing a feature.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Kibana and Elasticsearch: See What Tweets Can Say About a Conference</title>
   <link href="http://blog.florian-hopf.de/2013/09/kibana-and-elasticsearch-see-what.html"/>
   <updated>2013-09-20T14:38:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/09/kibana-and-elasticsearch-see-what</id>
   <content type="html">&lt;p&gt;In &lt;a href=&quot;/2013/09/simple-event-analytics-with.html&quot;&gt;my last post&lt;/a&gt; I showed how you can index tweets for an event in &lt;a href=&quot;http://elasticsearch.org&quot;&gt;Elasticsearch&lt;/a&gt; and how to do some simple queries on it using its HTTP API. This week I will show how you can use &lt;a href=&quot;http://three.kibana.org&quot;&gt;Kibana 3&lt;/a&gt; to visualize the data and make it explorable without having to learn the Elasticsearch API.&lt;/p&gt; &lt;h4&gt;Installing Kibana&lt;/h4&gt; &lt;p&gt;Kibana 3 is a pure HTML/JS frontend for Elasticsearch that you can use to build dashboards for your data. We are still working with the example data the is indexed using the Twitter River. It consists of tweets for &lt;a href=&quot;http://froscon.org&quot;&gt;FrOSCon&lt;/a&gt; but can be anything, especially data that contains some kind of timestamp as it's the case for tweets. To install Kibana you can just fetch it from the GitHub repostory (Note: now there are also &lt;a href=&quot;http://download.elasticsearch.org/kibana/kibana/kibana-latest.zip&quot;&gt;prepackaged archives&lt;/a&gt; available that you can download without cloning the repository):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;git clone https://github.com/elasticsearch/kibana.git&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You will now have a folder kibana that contains the html files as well as all the assets needed. The files need to be served by a webserver so you can just copy the folder to the directory e.g. Apache is serving. If you don't have a webserver installed you can simply serve the current directory using python:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;python -m SimpleHTTPServer 8080&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This will make Kibana available at &lt;a href=&quot;http://localhost:8080/kibana/src&quot;&gt;http://localhost:8080/kibana/src&lt;/a&gt;. With the default configuration Elasticsearch needs to be running on the same machine as well.&lt;/p&gt; &lt;h4&gt;Dashboards&lt;/h4&gt; &lt;p&gt;A dashboard in Kibana consists of rows that can contain different panels. Each panel can either display data, control which data is being displayed or both. Panels do not stand on their own; the results that are getting displayed are the same for the whole dashboard. So if you choose something in one panel you will notice that the other panels on the page will also get updated with new values.&lt;/p&gt; &lt;p&gt;When accessing Kibana you are directed to a welcome page from where you can choose between several dashboard templates. As Kibana is often used for logfile analytics there is an existing dashboard that is preconfigured to work with Logstash data. Another generic dashboard can be used to query some data from the index but we'll use the option &quot;Unconfigured Dashboard&quot; which gives some hints on which panels you might want to have.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-OQSUVJHYPuo/Ujvnh5nIFBI/AAAAAAAAAJ0/JkqwXBNzQHw/s1600/screenshot-kibana-intro.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-OQSUVJHYPuo/Ujvnh5nIFBI/AAAAAAAAAJ0/JkqwXBNzQHw/s640/screenshot-kibana-intro.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;This will present you with a dashboard that contains some rows and panels already.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-qWRCO45THU8/UjvnnNnRLvI/AAAAAAAAAJ8/qtYpaq2jJ9c/s1600/screenshot-basic-dashboard.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-qWRCO45THU8/UjvnnNnRLvI/AAAAAAAAAJ8/qtYpaq2jJ9c/s640/screenshot-basic-dashboard.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Starting from the top it contains these rows:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;The &quot;Options&quot; row that contains one text panel&lt;/li&gt;&lt;li&gt;The &quot;Query&quot; row that contains a text query panel&lt;/li&gt;&lt;li&gt;A hidden &quot;Filter&quot; row that contains a text panel and the filter panel. The row can be toggled visible by clicking on the text Filter on the left.&lt;/li&gt;&lt;li&gt;The &quot;Graph&quot; row two text panels&lt;/li&gt;&lt;li&gt;The large &quot;Table&quot; row with one text panel.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Those panels are already laid out in a way that they can display the widgets that are described in the text. We will now add those to get some data from the event tweets.&lt;/p&gt; &lt;h4&gt;Building a Dashboard&lt;/h4&gt; &lt;p&gt;The text panels are only there to guide you when adding the widgets you need and can then be removed. To add or remove panels for a row you can click the little gear next to the title of the row. This will open an options menu. For the top row we are choosing a timepicker panel with a default mode of absolute. This gives you the opportunity to choose a begin and end date for your data. The field that contains the timestamp is called &quot;created_at&quot;. After saving you can also remove the text panel on the second tab.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-59Xq1VvIZQA/Ujvn6POVx3I/AAAAAAAAAKA/_nWy6yZ5opc/s1600/screenshot-timepicker.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-59Xq1VvIZQA/Ujvn6POVx3I/AAAAAAAAAKA/_nWy6yZ5opc/s640/screenshot-timepicker.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;If you now open the &quot;Filters&quot; row you will see that there now is a filter displayed. It is best to keep this row open to see which filters are currently applied. You can remove the text panel in the row.&lt;/p&gt;   &lt;p&gt;In the graph section we will add two graph panels instead of the text panels: A pie chart that displays the terms of the tweet texts and a date histogram that shows how many tweets there are for a certain time. For the pie chart we use the field &quot;text&quot; and exclude some common terms again. Note that if you are adding terms to the excluded terms when the panel is already created that you need to initiate another query, e.g. by clicking the button in the timepicker. For the date histogram we are again choosing the timestamp field &quot;created_at&quot;. &lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-w7NjvRFj92g/Ujvn92FjhSI/AAAAAAAAAKI/O3gHLkOcvDs/s1600/screenshot-pie.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-w7NjvRFj92g/Ujvn92FjhSI/AAAAAAAAAKI/O3gHLkOcvDs/s640/screenshot-pie.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Finally, in the last row we are adding a table to display the resulting tweet documents. Besides adding the columns &quot;text&quot;, &quot;user.screen_name&quot; and &quot;created_at&quot; we can leave the settings like it's proposed.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-v2aT8LrQYMg/UjvoCOJ8O6I/AAAAAAAAAKQ/2qJd_x7C78U/s1600/screenshot-table.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-v2aT8LrQYMg/UjvoCOJ8O6I/AAAAAAAAAKQ/2qJd_x7C78U/s640/screenshot-table.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;We now have a dashboard to play with the data and see the results immediately. Data can be explored by using any of the displays, you can click in the pie chart to choose a certain term or choose a time range in the date histogram. This makes it really easy to work with the data.&lt;/p&gt; &lt;h4&gt;Answering questions&lt;/h4&gt; &lt;p&gt;Now we have a visual representation of all the terms and the time of day people are tweeting most. As you can see, people are tweeting slightly more during the beginning of the day.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-4GsQ23kD2qI/Ujvrm2yg7EI/AAAAAAAAAKs/anWPoqUyhrs/s1600/screenshot-tweets.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-4GsQ23kD2qI/Ujvrm2yg7EI/AAAAAAAAAKs/anWPoqUyhrs/s640/screenshot-tweets.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;You can now check for any relevant terms that you might be interested in. For example, let's see when people tweet about beer. As we do have tweets in multiple languages (german, english and people from cologne) we need to add some variation. We can enter the query &lt;pre&gt;text:bier* OR text:beer* OR text:kölsch&lt;/pre&gt; in the query box.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-UbcqvFM9tgQ/UjvoQOz379I/AAAAAAAAAKY/1Sxt1hFZBMg/s1600/screenshot-bier.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-UbcqvFM9tgQ/UjvoQOz379I/AAAAAAAAAKY/1Sxt1hFZBMg/s640/screenshot-bier.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;There are only few tweets about it but it will be a total surprise to you that most of the tweets about beer tend to be send later during the day (I won't go into detail why there are so many tweets mentioning the terms horse and piss when talking about Kölsch).&lt;/p&gt; &lt;p&gt;Some more surprising facts: There is not a single tweet mentioning Java but a lot of tweets that mention php, especially during the first day. This day seems to be far more successful for the PHP dev room.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-034G7aZqkFk/UjvoUSX14mI/AAAAAAAAAKg/TxMgPn1M_zI/s1600/screenshot-php.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-034G7aZqkFk/UjvoUSX14mI/AAAAAAAAAKg/TxMgPn1M_zI/s640/screenshot-php.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Summary&lt;/h4&gt; &lt;p&gt;I hope that I could give you some hints on how powerful Kibana can be when it comes to analytics of data, not only with log data. If you'd like to read another detailed step by step guide on using Kibana to visualize Twitter data have a look at &lt;a href=&quot;http://lbroudoux.wordpress.com/2013/04/30/real-time-analytics-with-elasticsearch-and-kibana3/&quot;&gt;this article by Laurent Broudoux.&lt;/a&gt;&lt;/p&gt; </content>
 </entry>
 
 <entry>
   <title>Simple Event Analytics with ElasticSearch and the Twitter River</title>
   <link href="http://blog.florian-hopf.de/2013/09/simple-event-analytics-with.html"/>
   <updated>2013-09-11T14:25:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/09/simple-event-analytics-with</id>
   <content type="html">&lt;p&gt;Tweets can say a lot about an event. The hashtags that are used and the time that is used for tweeting can be interesting to see.   Some of the questions you might want answers to: &lt;ul&gt;&lt;li&gt;Who tweeted the most?&lt;/li&gt;&lt;li&gt;What are the dominant keywords/hashtags?&lt;/li&gt;&lt;li&gt;When is the time people are tweeting the most?&lt;/li&gt;&lt;li&gt;And, most importantly: Is there a correlation between the time and the amount of tweets mentioning coffee or beer?&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;During this years &lt;a href=&quot;http://froscon.org&quot;&gt;FrOSCon&lt;/a&gt; I indexed all relevant tweets in &lt;a href=&quot;http://elasticsearch.org&quot;&gt;ElasticSearch&lt;/a&gt; using the &lt;a href=&quot;https://github.com/elasticsearch/elasticsearch-river-twitter&quot;&gt;Twitter River&lt;/a&gt;. In this post I'll show you how you can index tweets in ElasticSearch to have a dataset you can do analytics with. We will see how we can get answers to the first two questions using the ElasticSearch Query DSL. Next week I will show how &lt;a href=&quot;http://three.kibana.org&quot;&gt;Kibana&lt;/a&gt; can help you to get a visual representation of the data.&lt;/p&gt; &lt;h4&gt;Indexing Tweets in ElasticSearch&lt;/h4&gt; &lt;p&gt;To run ElasticSearch you need to have a recent version of Java installed. Then you can just &lt;a href=&quot;http://www.elasticsearch.org/download/&quot;&gt;download the archive&lt;/a&gt; and unpack it. It contains a bin directory with the necessary scripts to start ElasticSearch:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;bin/elasticsearch -f&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;-f will take care that ElasticSearch starts in the foreground so you can also stop it using Ctrl-C. You can see if your installation is working by calling &lt;a href=&quot;http://localhost:9200&quot;&gt;http://localhost:9200&lt;/a&gt; in your browser.&lt;/p&gt; &lt;p&gt;After stopping it again we need to install the ElasticSearch Twitter River that uses the Twitter streaming API to get all the tweets we are interested in.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;bin/plugin -install elasticsearch/elasticsearch-river-twitter/1.4.0&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Twitter doesn't allow anonymous access to its API anymore so you need to register for the OAuth access at &lt;a href=&quot;https://dev.twitter.com/apps&quot;&gt;https://dev.twitter.com/apps&lt;/a&gt;. Choose a name for your application and generate the key and token. Those will be needed to configure the plugin via the REST API. In the configuration you need to pass your OAuth information as well as any keyword you would like to track and the index that should be used to store the data.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPUT localhost:9200/_river/frosconriver/_meta -d '&lt;br /&gt;{&lt;br /&gt;    &quot;type&quot; : &quot;twitter&quot;,&lt;br /&gt;    &quot;twitter&quot; : {&lt;br /&gt;        &quot;oauth&quot; : {&lt;br /&gt;            &quot;consumer_key&quot; : &quot;YOUR_KEY&quot;,&lt;br /&gt;            &quot;consumer_secret&quot; : &quot;YOUR_SECRET&quot;,&lt;br /&gt;            &quot;access_token&quot; : &quot;YOUR_TOKEN&quot;,&lt;br /&gt;            &quot;access_token_secret&quot; : &quot;YOUR_TOKEN_SECRET&quot;&lt;br /&gt;        },&lt;br /&gt;        &quot;filter&quot; : {&lt;br /&gt;            &quot;tracks&quot; : &quot;froscon&quot;&lt;br /&gt;        }&lt;br /&gt;    },&lt;br /&gt;    &quot;index&quot; : {&lt;br /&gt;        &quot;index&quot; : &quot;froscon&quot;,&lt;br /&gt;        &quot;type&quot; : &quot;tweet&quot;,&lt;br /&gt;        &quot;bulk_size&quot; : 1&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The index doesn't need to exist yet, it will be created automatically. I am using a bulk size of 1 as there aren't really many tweets. If you are indexing a lot of data you might consider setting this to a higher value.&lt;/p&gt; &lt;p&gt;After issuing the call you should see some information in the logs that the river is starting and receiving data. You can see how many tweets there are in your index by issuing a count query:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'localhost:9200/froscon/_count?pretty=true&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can see the basic structure of the documents created by looking at the mapping that is created automatically.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;http://localhost:9200/froscon/_mapping?pretty=true&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The result is quite long so I am not replicating it here but it contains all the relevant information you might be interested in like the user who tweeted, the location of the user, the text, the mentions and any links in it.&lt;/p&gt; &lt;h4&gt;Doing Analytics Using the ElasticSearch REST API&lt;/h4&gt; &lt;p&gt;Once you have enough tweets indexed you can already do some analytics using the ElasticSearch REST API and the Query DSL. This requires you to have some understanding of the query syntax but you should be able to get started by skimming through &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/query-dsl/&quot;&gt;the documentation&lt;/a&gt;.&lt;/p&gt; &lt;h5&gt;Top Tweeters&lt;/h5&gt; &lt;p&gt;First, we'd like to see who tweeted the most. This can be done by doing a query for all documents and facet on the user name. This will give us the names and count in a section of the response.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -X POST &quot;http://localhost:9200/froscon/_search?pretty=true&quot; -d '&lt;br /&gt;  {&lt;br /&gt;    &quot;size&quot;: 0,&lt;br /&gt;    &quot;query&quot; : {&lt;br /&gt;      &quot;match_all&quot; : {}&lt;br /&gt;    },&lt;br /&gt;    &quot;facets&quot; : {&lt;br /&gt;      &quot;user&quot; : { &lt;br /&gt;        &quot;terms&quot; : {&lt;br /&gt;          &quot;field&quot; : &quot;user.screen_name&quot;&lt;br /&gt;        } &lt;br /&gt;      }                            &lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Those are the top tweeters for FrOSCon:&lt;p/&gt; &lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://twitter.com/ElectricMaxxx&quot;&gt;ElectricMaxxx&lt;/a&gt; (36)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://twitter.com/froscon&quot;&gt;FrOSCon&lt;/a&gt; (20)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://twitter.com/slashme&quot;&gt;slashme&lt;/a&gt; (18)&lt;/li&gt;&lt;/ul&gt; &lt;h5&gt;Dominant Keywords&lt;/h5&gt; &lt;p&gt;The dominant keywords can also be retrieved using a facet query, this time on the text of the tweet. As there are a lot of german tweets for FrOSCon and the text field is processed using the StandardAnalyzer that only removes english stopwords it might be necessary to exclude some terms. Also you might want to remove some other common terms that indicate retweets or are part of urls.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -X POST &quot;http://localhost:9200/froscon/_search?pretty=true&quot; -d '&lt;br /&gt;  {&lt;br /&gt;    &quot;size&quot;: 0,&lt;br /&gt;    &quot;query&quot; : {&lt;br /&gt;      &quot;match_all&quot; : {}&lt;br /&gt;    },&lt;br /&gt;    &quot;facets&quot; : {&lt;br /&gt;      &quot;keywords&quot; : { &lt;br /&gt;        &quot;terms&quot; : {&lt;br /&gt;          &quot;field&quot; : &quot;text&quot;, &lt;br /&gt;          &quot;exclude&quot; : [&quot;froscon&quot;, &quot;rt&quot;, &quot;t.co&quot;, &quot;http&quot;, &quot;der&quot;, &quot;auf&quot;, &quot;ich&quot;, &quot;my&quot;, &quot;die&quot;, &quot;und&quot;, &quot;wir&quot;, &quot;von&quot;] &lt;br /&gt;        }&lt;br /&gt;      }                            &lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Those are the dominant keywords for FrOSCon:&lt;p/&gt; &lt;ul&gt;&lt;li&gt;talk (no surprise for a conference)&lt;/li&gt;&lt;li&gt;slashme&lt;/li&gt;&lt;li&gt;teamix (a company that does very good marketing. Unfortunately in this case this is more because their fluffy tux got stolen. The tweet about it is the most retweeted tweet of the data.)&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;Summary&lt;/h4&gt; &lt;p&gt;Using the Twitter River it is really easy to get some data into ElasticSearch. The Query DSL makes it easy to extract some useful information. Next week we will have a look at Kibana that doesn't necessarily require a deep understanding of the ElasticSearch queries and can visualize our data.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Developing with CoreMedia</title>
   <link href="http://blog.florian-hopf.de/2013/09/developing-with-coremedia.html"/>
   <updated>2013-09-04T12:01:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/09/developing-with-coremedia</id>
   <content type="html">&lt;p&gt;A while ago I had the chance to attend a training on web development with &lt;a href=&quot;http://coremedia.com&quot;&gt;CoreMedia&lt;/a&gt;. It's a quite enterprisey commercial Content Management System that powers large corporate websites like telekom.com as well as news sites like Bild.de (well, you can't hold CoreMedia responsible for the kind of &quot;content&quot; people put into their system). As I have been working with different Java based Content Management Systems over the years I was really looking forward to learn about the system I heard really good things about. In this post I'll describe the basic structure of the system as well how it feels like to develop with it.&lt;/p&gt; &lt;h4&gt;System Architecture&lt;/h4&gt; &lt;p&gt;As CoreMedia is built to scale to really large sites the architecture is also built around redundant and distributed components. The part of the system the editors are working on is seperated from the parts that serve the content to the internet audience. A publication process copies the content from the editorial system to the live system.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-3kz-sNUhiAU/Uiau6PoAbNI/AAAAAAAAAJg/1m0ZbGiw3MI/s1600/coremedia.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-3kz-sNUhiAU/Uiau6PoAbNI/AAAAAAAAAJg/1m0ZbGiw3MI/s1600/coremedia.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;The heart of CoreMedia is the Content Server. It stores all the content in a database and makes it retrievable. You rarely access it directly but only via other applications that then talk to it in the background via CORBA. Editors used to work with CoreMedia using a Java client (used to be called the Editor, now known as the Site Manager), starting with CoreMedia 7 there is also the web based Studio that is used to create and edit content. A preview application can be used to see how the site looks before being published. Workflows, that are managed using the Workflow Server, can be used to control the processes around editing as well as publication.&lt;/p&gt; &lt;p&gt;The live system consists of several components that are mostly laid out in a redundant way. There is one Master Live Server as well as 0 to n Replication Live Servers that are used for distributing the load as well as fault tolerance. The Content Management Servers are accessed from the Content Application Engine (CAE) that contains all the delivery and additional logic for your website. One or more Solr instances are used to provide the search services for your application.&lt;/p&gt; &lt;h4&gt;Document Model&lt;/h4&gt; &lt;p&gt;The document model for your application describes the content types that are available in the system. CoreMedia provides a blueprint application that contains a generic document model that can be used as a basis for your application but you are also free to build something completely different. The document model is used throughout the whole system as it describes the way your content is stored. The model is object oriented in nature with documents that consist of attributes. There are 6 attribute types like String (fixed length Strings), XML (variable length Strings) and Blob (binary data) available that form the basis of all your types. An XML configuration file is used to describe your specific document model. This is an example of an article that contains a title, the text and a list of related articles.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;DocType Name=&amp;quot;Article&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;StringProperty Name=&amp;quot;title&amp;quot;/&amp;gt;&lt;br /&gt;  &amp;lt;XmlProperty Grammar=&amp;quot;coremedia-richtext-1.0&amp;quot; Name=&amp;quot;text&amp;quot;/&amp;gt;&lt;br /&gt;  &amp;lt;LinkListProperty LinkType=&amp;quot;Article&amp;quot; Name=&amp;quot;related&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/DocType&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;Content Application Engine&lt;/h4&gt; &lt;p&gt;Most of the code you will be writing is the delivery code that is part of the Content Application Engine, either for preview or for the live site. This is a standard Java webapp that is assembled from different Maven based modules. CAE code is heavily based on Spring MVC with the CoreMedia specific View Dispatcher that takes care of the rendering of different documents. The document model is made available using the so called Contentbeans that can be generated from the document model. Contentbeans access the content on demand and can contain additional business logic. So those are no POJOs but more active objects similar to Active Record entities in the Rails world.&lt;/p&gt; &lt;p&gt;Our example above would translate to a Contentbean with getters for the title (a java.lang.String), the text (a com.coremedia.xml.Markup) and a getter for a java.util.List that is typed to de.fhopf.Article.&lt;/p&gt; &lt;p&gt;Rendering of the Contentbeans happens in JSPs that are named according to classes or interfaces with a specific logic to determine which JSP should be used. An object Article that resides in the package de.fhopf would then be found in the path de/fhopf/Article.jsp, if you want to add a special rendering mechanism for List this would be in java/util/List.jsp. Different rendering of objects can be done by using a view name. An Article that is rendered as a link would then be in de/fhopf/Artilcle.link.jsp.&lt;/p&gt;   &lt;p&gt;This is done using one of the custom Spring components of CoreMedia, the View Dispatcher, a View Resolver that determines the correct view to be invoked for a certain model based on the content element in the Model. The JSP that is used can then contain further includes on other elements of the content, be it documents in the sense of CoreMedia or one of the attributes that are available. Those includes are again routed through the View Dispatcher.&lt;/p&gt; &lt;p&gt;Let's see an example for rendering the list of related articles for an article. Say you call the CAE with a certain content id, that is an Article. The standard mechanism routes this request to the Article.jsp described above. It might contain the following fragment to include the related articles:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;cm:include self=&amp;quot;${self.related}&amp;quot;/&amp;gt;&lt;/pre&gt;&lt;/code&gt; &lt;p&gt;Note that we do not tell which JSP to include. CoreMedia automatically figures out that we are including a List, for example a java.util.ArrayList. As there is no JSP available at java/util/ArrayList.jsp Coremedia will automatically look for any interfaces that are implemented by that class, in this case it will find java/util/List.jsp. This could then contain the following fragment:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;ul&amp;gt;&lt;br /&gt;&amp;lt;c:forEach items=&amp;quot;${self}&amp;quot; var=&amp;quot;item&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;li&amp;gt;&amp;lt;cm:include self=&amp;quot;${item}&amp;quot; view=&amp;quot;link&amp;quot;&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/c:forEach&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;As the List in our case contains Article implementations this will then hit the Article.link.jsp that would finally render the link. This is a very flexible approach with a high degree of reusability for the fragments. The List.jsp we are seeing above has no connection to the Article. You can use it for any objects that should be rendered in a List structure, the View Dispatcher of CoreMedia takes care of which JSP to include for a certain type.&lt;/p&gt; &lt;p&gt;To minimize the load on the Content Server you can also add caching via configuration settings. Data Views, that are a layer on top of the Contentbeans, are then held in memory and contain prefilled beans that don't need to access the Content Management Server anymore. This object cache approach is different to the html fragment caching a lot of other systems are doing.&lt;/p&gt; &lt;h4&gt;Summary&lt;/h4&gt; &lt;p&gt;Though this is only a very short introduction you should have seen that CoreMedia really is a nice system to work with. The distributed nature not only makes it scalable but this also has implications when developing for it: When you are working on the CAE you are only changing code in this component. You can start the more heavyweight Contentserver only once and afterwards work with the lightweight CAE that can be run using the Maven jetty plugin. Restarts don't take a long time so you have short turnaround times. The JSPs are very cleanly structured and don't need to include scriptlets (I heard that this has been different for earlier versions). As most of the application is build around Spring MVC you can use a lot of knowledge that is around already.&lt;/p&gt;  </content>
 </entry>
 
 <entry>
   <title>FrOSCon 8 2013 - Free and Open Source Software Conference</title>
   <link href="http://blog.florian-hopf.de/2013/08/froscon-8-2013-free-and-open-source.html"/>
   <updated>2013-08-28T14:35:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/08/froscon-8-2013-free-and-open-source</id>
   <content type="html">&lt;p&gt;Last weekend I attended &lt;a href=&quot;http://www.froscon.de/en/home/&quot;&gt;FrOSCon&lt;/a&gt;, the Free and Open Source Software Conference taking place in St. Augustin near Bonn. It's a community organized conference with an especially low entrance fee and a relaxed vibe. The talks are a good mixture of development and system administration topics.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-DMitreQyzGg/Uh1_tR3EU9I/AAAAAAAAAI4/J3hs8zoRv4s/s1600/2013-08-25+13.46.50.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/-DMitreQyzGg/Uh1_tR3EU9I/AAAAAAAAAI4/J3hs8zoRv4s/s320/2013-08-25+13.46.50.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Some of the interesting talks I attended:&lt;/p&gt; &lt;h4&gt;Fixing Legacy Code by Kore Nordmann and Benjamin Eberlein&lt;/h4&gt; &lt;p&gt;Though this session was part of the PHP track it contained a lot of valuable information related to working with legacy code in any language. Besides strategies for getting an application under test the speakers showed some useful refactorings that can make sense to start with. &lt;a href=&quot;http://qafoo.com/talks/13_08_froscon_fixing_legacy_code.pdf&quot;&gt;Slides&lt;/a&gt;&lt;/p&gt; &lt;h4&gt;Building Awesome Ruby Command Line Apps by Christian Vervoorts&lt;/h4&gt; &lt;p&gt;Christian first showed some of the properties that make up a good command line app. You should choose sane default values but make those configurable. Help functionality is crucial for a good user experience, via -h parameter and a man page. In the second part Chistian introduced some Ruby gems that can be used to build command line apps. &lt;a href=&quot;https://github.com/davetron5000/gli&quot;&gt;GLI&lt;/a&gt; seems to be the most interesting with a nice DSL and its scaffolding functionality.&lt;/p&gt; &lt;h4&gt;Talking People Into Creating Patches by Isabel Drost-Fromm&lt;/h4&gt; &lt;p&gt;Isabel, who is very active in the Apache community, introduced some of her findings when trying to make students, researchers and professionals participate in Open Source. The participants where a mixture of people running open source projects and developers that are interested in contributing to open source. I have been especially interested in this talk because I wouldn't mind having more people help with the &lt;a href=&quot;http://incubator.apache.org/odftoolkit/&quot;&gt;Odftoolkit&lt;/a&gt; I am also working on. When working with professionals, who are the main target, it is important to answer quickly on mails or issues as they might move on to other projects and might not be able to help later on. Also, it's nice to have some easy tasks in the bugtracker that can be processed by newbies.&lt;/p&gt; &lt;h4&gt;MySQL Performance Schema by Carsten Thalheimer&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://dev.mysql.com/doc/refman/5.6/en/performance-schema.html&quot;&gt;Performance Schema&lt;/a&gt; is a new feature in MySQL 5.5 and is activated by default since 5.6. It monitors a lot of the internal functionality like file access and queries so you can later see which parts you can optimize. Some performance measurements done by the MySQL developers showed that keeping it activated has an performance impact of around 5%. Though this doesn't sound that good at first I think you can gain a lot more performance by the insight you have in the inner workings. Working with Performance Schema is supposed to be rather complex (&quot;Take two weeks to work with it&quot;), &lt;a href=&quot;http://www.markleith.co.uk/ps_helper/&quot;&gt;ps_helper&lt;/a&gt; is a more beginner friendly functionality that can get you started with some useful metrics.&lt;/p&gt; &lt;h4&gt;Summary&lt;/h4&gt; &lt;p&gt;FrOSCon is the one of the most relaxing conferences I know. It is my goto place for seeing stuff that is not directly related to Java development. The low fee makes it a no brainer to attend. If you are interested in any of this years talks they will also be made available online.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Getting Started with ElasticSearch: Part 2 - Querying</title>
   <link href="http://blog.florian-hopf.de/2013/08/getting-started-with-elasticsearch-part.html"/>
   <updated>2013-08-21T14:15:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/08/getting-started-with-elasticsearch-part</id>
   <content type="html">&lt;p&gt;This is the second part of the article on things I learned while building a simple Java based search application on top of &lt;a href=&quot;http://elasticsearch.org&quot; target=&quot;_blank&quot;&gt;ElasticSearch&lt;/a&gt;. In the &lt;a href=&quot;http://blog.florian-hopf.de/2013/05/getting-started-with-elasticsearch-part.html&quot; target=&quot;_blank&quot;&gt;first part of this article&lt;/a&gt; we looked at how to index data in ElasticSearch and what the mapping is. Though ElasticSearch is often called schema free specifying the mapping is still a crucial part of creating a search application. This time we will look at the query side and see how we can get our indexed talks out of it again.&lt;/p&gt; &lt;h4&gt;Simple Search&lt;/h4&gt; &lt;p&gt;Recall that our documents consist of a title, the date and the speaker of a talk. We have adjusted the mapping so that for the title we are using the German analyzer that stems our terms and we can search on variations of words. This curl request creates a similar index:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPUT &quot;http://localhost:9200/blog&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;mappings&quot; : {&lt;br /&gt;        &quot;talk&quot; : {&lt;br /&gt;            &quot;properties&quot; : {&lt;br /&gt;                &quot;title&quot; : { &quot;type&quot; : &quot;string&quot;, &quot;store&quot; : &quot;yes&quot;, &quot;analyzer&quot; : &quot;german&quot; }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Let's see how we can search on our content. We are indexing another document with a German title.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/blog/talk/&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;    &quot;date&quot; : &quot;2012-07-04T19:15:00&quot;,&lt;br /&gt;    &quot;title&quot; : &quot;Suchen und Finden mit Lucene und Solr&quot;&lt;br /&gt;}'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;All searching is done on the &lt;code&gt;_search&lt;/code&gt; endpoint that is available on the type or index level (you can also search on multiple types and indexes by separating them with a comma). As the title field uses the German analyzer we can search on variations of the words, e.g. suche which stems to the same root as suchen, such.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/blog/talk/_search?q=title:suche&amp;pretty=true&quot;                                                                       &lt;br /&gt;{&lt;br /&gt;  &quot;took&quot; : 14, &lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;},                                                                                                                                                                                                                             &lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 1,&lt;br /&gt;    &quot;max_score&quot; : 0.15342641,&lt;br /&gt;    &quot;hits&quot; : [ {&lt;br /&gt;      &quot;_index&quot; : &quot;blog&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;talk&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;A2Qv3fN3TkeYEhxA4zicgw&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.15342641, &quot;_source&quot; : {&lt;br /&gt;        &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;        &quot;date&quot; : &quot;2012-07-04T19:15:00&quot;,&lt;br /&gt;        &quot;title&quot; : &quot;Suchen und Finden mit Lucene und Solr&quot;&lt;br /&gt;      }&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;The _all field&lt;/h4&gt; &lt;p&gt;Now that this works, we might want to search on multiple fields. ElasticSearch provides the convenience functionality of copying all field content to the &lt;code&gt;_all&lt;/code&gt; field that is used when omitting the field name in the query. Let's try the query again:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/blog/talk/_search?q=suche&amp;pretty=true&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;took&quot; : 3,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 0,&lt;br /&gt;    &quot;max_score&quot; : null,&lt;br /&gt;    &quot;hits&quot; : [ ]&lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;No results. Why is that? Of course we have set the analyzer correctly for the title as we have seen above. But this doesn't mean that the content is analyzed in the same way for the &lt;code&gt;_all&lt;/code&gt; field. As we didn't specify an analyzer for this field it still uses the &lt;code&gt;StandardAnalyzer&lt;/code&gt; that splits on whitespace but doesn't do any stemming. If you want to have a consistent behavior for the title and the &lt;code&gt;_all&lt;/code&gt; field you need to set the analyzer in the mapping:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPUT &quot;http://localhost:9200/blog/talk/_mapping&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;mappings&quot; : {&lt;br /&gt;        &quot;talk&quot; : {&lt;br /&gt;            &quot;_all&quot; : {&quot;analyzer&quot; : &quot;german&quot;},&lt;br /&gt;            &quot;properties&quot; : {&lt;br /&gt;                &quot;title&quot; : { &quot;type&quot; : &quot;string&quot;, &quot;store&quot; : &quot;yes&quot;, &quot;analyzer&quot; : &quot;german&quot; }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Note that as with all mapping changes you can't change the type of the &lt;code&gt;_all&lt;/code&gt; field once it's created. You need to delete the index, put the new mapping and reindex your data. Afterwards our search will return the same results for the two queries.&lt;/p&gt;   &lt;h4&gt;_source&lt;/h4&gt; &lt;p&gt;You might have noticed from the example above that ElasticSearch returns the special &lt;code&gt;_source&lt;/code&gt; field for each result. This is very convenient as you don't need to specify which fields should be stored. But be aware that this might become a problem for large fields that you don't need for each search request (content section of articles, images that you might store in the index). You can either disable the use of the source field and indicate which fields should be stored in the mapping for your indexed type or you can specify in the query which fields you'd like to retrieve:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET &quot;http://localhost:9200/blog/talk/_search?q=suche&amp;pretty=true&amp;fields=speaker,title&quot;&lt;br /&gt;{&lt;br /&gt;  &quot;took&quot; : 6,&lt;br /&gt;  &quot;timed_out&quot; : false,&lt;br /&gt;  &quot;_shards&quot; : {&lt;br /&gt;    &quot;total&quot; : 5,&lt;br /&gt;    &quot;successful&quot; : 5,&lt;br /&gt;    &quot;failed&quot; : 0&lt;br /&gt;  },&lt;br /&gt;  &quot;hits&quot; : {&lt;br /&gt;    &quot;total&quot; : 2,&lt;br /&gt;    &quot;max_score&quot; : 0.15342641,&lt;br /&gt;    &quot;hits&quot; : [ {&lt;br /&gt;      &quot;_index&quot; : &quot;blog&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;talk&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;MA2oYAqnTdqJhbjnCNq2zA&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.15342641&lt;br /&gt;    }, {&lt;br /&gt;      &quot;_index&quot; : &quot;blog&quot;,&lt;br /&gt;      &quot;_type&quot; : &quot;talk&quot;,&lt;br /&gt;      &quot;_id&quot; : &quot;aGdDy24cSImz6DVNSQ5iwA&quot;,&lt;br /&gt;      &quot;_score&quot; : 0.076713204,&lt;br /&gt;      &quot;fields&quot; : {&lt;br /&gt;        &quot;speaker&quot; : &quot;Florian Hopf&quot;,&lt;br /&gt;        &quot;title&quot; : &quot;Suchen und Finden mit Lucene und Solr&quot;&lt;br /&gt;      }&lt;br /&gt;    } ]&lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The same can be done if you are not using the simple query parameters but the more advanced query DSL:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/blog/talk/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;fields&quot; : [&quot;title&quot;, &quot;speaker&quot;],&lt;br /&gt;    &quot;query&quot; : {&lt;br /&gt;        &quot;term&quot; : { &quot;speaker&quot; : &quot;florian&quot; }&lt;br /&gt;    }&lt;br /&gt;}'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;Querying from Java&lt;/h4&gt;   &lt;p&gt;Besides the JSON based Query DSL you can also query ElasticSearch using Java. The default ElasticSearch Java client provides builders for creating different parts of the query that can then be combinded. For example if you'd like to query on two fields using the &lt;code&gt;multi_match&lt;/code&gt; query this is what it looks like using curl:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST &quot;http://localhost:9200/blog/_search&quot; -d'&lt;br /&gt;{&lt;br /&gt;    &quot;query&quot; : {&lt;br /&gt;        &quot;multi_match&quot; : {&lt;br /&gt;            &quot;query&quot; : &quot;Solr&quot;,&lt;br /&gt;            &quot;fields&quot; : [ &quot;title&quot;, &quot;speaker&quot; ]&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The Java version maps quite well to this. Once you found the builders you need you can use the &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/query-dsl/&quot; target=&quot;_blank&quot;&gt;excellent documentation of the Query DSL&lt;/a&gt; for your Java client as well.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;QueryBuilder multiMatch = multiMatchQuery(&quot;Solr&quot;, &quot;title&quot;, &quot;speakers&quot;);&lt;br /&gt;SearchResponse response = esClient.prepareSearch(&quot;blog&quot;)&lt;br /&gt;        .setQuery(multiMatch)&lt;br /&gt;        .execute().actionGet();&lt;br /&gt;assertEquals(1, response.getHits().getTotalHits());&lt;br /&gt;SearchHit hit = response.getHits().getAt(0);&lt;br /&gt;assertEquals(&quot;Suchen und Finden mit Lucene und Solr&quot;, hit.getSource().get(&quot;title&quot;));&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt; &lt;p&gt;The same &lt;code&gt;QueryBuilder&lt;/code&gt; we are constructing above can also be used on other parts of the query: For example it can be passed as a parameter to create a &lt;code&gt;QueryFilterBuilder&lt;/code&gt; or can be used to construct a &lt;code&gt;QueryFacetBuilder&lt;/code&gt;. This composition is a very powerful way to build flexible applications. It is easier to reason about the components of the query and you could even test parts of the query on its own.&lt;/p&gt; &lt;h4&gt;Faceting&lt;/h4&gt; &lt;p&gt;One of the most prominent features of ElasticSearch is its excellent faceting support that not only is used for building search applications but also for doing analytics of large data sets. You can use different kinds of faceting, e.g. for certain terms, using the &lt;code&gt;TermsFacet&lt;/code&gt;, or for queries, using the query facet. The query facet would accept the same &lt;code&gt;QueryBuilder&lt;/code&gt; that we used above.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;TermsFacetBuilder facet = termsFacet(&quot;speaker&quot;).field(&quot;speaker&quot;);&lt;br /&gt;QueryBuilder query = queryString(&quot;solr&quot;);&lt;br /&gt;SearchResponse response = esClient.prepareSearch(&quot;blog&quot;)&lt;br /&gt;        .addFacet(facet)&lt;br /&gt;        .setQuery(query)&lt;br /&gt;        .execute().actionGet();&lt;br /&gt;assertEquals(1, response.getHits().getTotalHits());&lt;br /&gt;SearchHit hit = response.getHits().getAt(0);&lt;br /&gt;assertEquals(&quot;Suchen und Finden mit Lucene und Solr&quot;, hit.getSource().get(&quot;title&quot;));&lt;br /&gt;TermsFacet resultFacet = response.getFacets().facet(TermsFacet.class, &quot;speaker&quot;);&lt;br /&gt;assertEquals(1, resultFacet.getEntries().size());&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;Conclusion&lt;/h4&gt; &lt;p&gt;ElasticSearch has a really nice Java API, be it for indexing or for querying. You can get started with indexing and searching in no time though you need to know some concepts or the results might not be what you expect.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>The Pragmatic Programmers Rubber Duck of the 19th Century</title>
   <link href="http://blog.florian-hopf.de/2013/08/the-pragmatic-programmers-rubber-duck.html"/>
   <updated>2013-08-14T14:02:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/08/the-pragmatic-programmers-rubber-duck</id>
   <content type="html">&lt;p&gt;In their influential book &lt;a href=&quot;http://pragprog.com/book/tpp/the-pragmatic-programmer&quot; target=&quot;_blank&quot;&gt;&quot;The Pragmatic Programmer&quot;&lt;/a&gt; Andy Hunt and Dave Thomas describe a technique for finding solutions to hard problems you are struggling with. They recommend to just tell the problem to somebody, not for getting an answer, but because while explaining the problem you are thinking differently about it. And, if there is nobody around you, get yourself a rubber duck you can talk to, hence the name of the tip.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-N0Ox1aOUhOU/UgsLxL8OCAI/AAAAAAAAAIs/YxQZziZkGZs/s1600/rubber-duck.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-N0Ox1aOUhOU/UgsLxL8OCAI/AAAAAAAAAIs/YxQZziZkGZs/s320/rubber-duck.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;It's obvious that this is not a new discovery made by the authors. Everybody has experienced similar situations where they are finding a solution to a problem while explaining it to someone. But I have been surprised to read this in the essay &quot;Über die allmähliche Verfertigung der Gedanken beim Reden&quot; by Heinrich von Kleist dating to 1805/1806 (translated from German by me):&lt;/p&gt; &lt;blockquote&gt;If you want to know something and you can't find it in meditation I advice you [...] to tell it to the next acquaintance you are meeting. He doesn't need to be a keen thinker and I don't mean you should ask him: no! Rather you should tell him about it in the first place.&lt;/blockquote&gt; &lt;p&gt;1806. The same tip (without the duck). This is another case where we are relearning things that have been discovered before, something especially computer science is prone to.&lt;/p&gt; &lt;p&gt;So, what is the big achievement of the authors? It's not that they are finding only new ideas. We don't need that many new ideas. There is a lot of stuff around that is just waiting to be applied to our work as software developers. Those old ideas need to be put into context. There is even a benefit in stating obvious things that might trigger rethinking your habits.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>SoCraTes 2013 - Two Days of Open Space</title>
   <link href="http://blog.florian-hopf.de/2013/08/socrates-2013-two-days-of-open-space.html"/>
   <updated>2013-08-11T20:23:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/08/socrates-2013-two-days-of-open-space</id>
   <content type="html">&lt;p&gt;Last week I attended &lt;a href=&quot;http://www.socrates-conference.de/&quot; target=&quot;_blank&quot;&gt;SoCraTes&lt;/a&gt;, an international Open Space conference on Software Craftmanship and Testing. As a few days have passed now I'd like to recap what sessions I attended and what was discussed. Though this is only a minor part of the sessions that have been discussed you should get a good grasp on what kind of topics are shared during the two days.&lt;/p&gt; &lt;h3&gt;General&lt;/h3&gt; &lt;p&gt;The conference is located at Seminarzentrum Rückersbach which is close to nowhere (Aschaffenburg). As there is nothing around you where people would normally go during conferences (pubs) everybody is staying on the premise for full 48h. You sleep there, you eat there, you spend your evening there. Generally you are around the same people from thursday evening to saturday evening which leads to a lot of interesting talks and evening activities.&lt;/p&gt; &lt;h3&gt;Open Space format&lt;/h3&gt; &lt;p&gt;Some of the time is needed for the framework of the Open Space. On thursday evening there is a world cafe, where you spend time on different tables with different people to discuss what you expect from the conference and what you would like to learn.&lt;/p&gt;   &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-5CLiqU6N1x0/Ugd_bV2dv-I/AAAAAAAAAH8/xDyihXP3BEk/s1600/marketplace.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-5CLiqU6N1x0/Ugd_bV2dv-I/AAAAAAAAAH8/xDyihXP3BEk/s320/marketplace.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Every day starts with the planning of the day, the marketplace. Every participant can propose talks, discussions and hands on sessions and put those on the schedule. There are several rooms available and, if the weather permits, there is also plenty of space outside for discussions. The day then is dedicated to the sessions, some of which I will describe below. In the evening there is some kind of retrospective of the day. Also you can propose evening activities, which can range from discussions, coding and board games.&lt;/p&gt; &lt;p&gt;The sessions I will describe now are only a snapshot of what has been available. As there are so many good things it's sometimes hard to decide which session to attend.&lt;/p&gt; &lt;h3&gt;Day 1&lt;/h3&gt; &lt;p&gt;&lt;a href=&quot;https://twitter.com/rhosts/status/363250042062131200/photo/1&quot; target=&quot;_blank&quot;&gt;Agenda for day 1&lt;/a&gt;, photo taken by Robert Hostlowsky, who also &lt;a href=&quot;http://blog.codecentric.de/en/2013/08/socrates2013/&quot; target=&quot;_blank&quot;&gt;blogged about SoCraTes over at the Codecentric blog.&lt;/a&gt;&lt;/p&gt; &lt;h4&gt;Continuous Delivery&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://shishkin.org/&quot; target=&quot;_blank&quot;&gt;Sergey&lt;/a&gt;, who did a lot of sessions for the event, discussed a problem he is facing when trying to implement Continuous Delivery. One of the basic concepts of CD is that you should be using the same artifact for all of your stages, that means an artifact should only be build once and deployed to the different systems for automated testing, user testing and finally deployment. When an artifact is promoted to one of the later stages you would like to update the version so it is obvious that it is a real release candidate. Depending on the technology you are using it might not be that easy to update the version without building the artifact again.&lt;/p&gt; &lt;h4&gt;Integrated Tests are a scam&lt;/h4&gt; &lt;p&gt;This session, inspired by &lt;a href=&quot;http://blog.thecodewhisperer.com/2010/10/16/integrated-tests-are-a-scam/&quot; target=&quot;_blank&quot;&gt;this article by J.B. Rainsberger&lt;/a&gt;, mostly revolved around the problems of testing database heavy applications, where some business logic tends to be contained in the database e.g. by means of OR mapping configurations. During the discussion it became obvious that the term integration test is too overloaded: A lot of people are thinking of integrating external systems like databases whereas others think of it as integrating some of your components. I learned about &lt;a href=&quot;http://alistair.cockburn.us/Hexagonal+architecture&quot; target=&quot;_blank&quot;&gt;Hexagonal Architecture&lt;/a&gt;  which I didn't know as a term before.&lt;/p&gt; &lt;h4&gt;Mapping Personal Practices&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.shino.de/&quot; target=&quot;_blank&quot;&gt;Markus Gärtner&lt;/a&gt; hosted a laid back outdoor session on determining in which areas of your professional life you would like to improve. First we collected all the stuff we are doing daily on a mind map and discussed them. Next we determined which parts we would like to improve in during the next months. I have done similar things for myself before but it was interesting to see what other people are working on and what they are interested in.&lt;/p&gt; &lt;h4&gt;Specification by Example Experience Report&lt;/h4&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-ipgBb6TIiJw/Ugd_l0Ci2GI/AAAAAAAAAIc/KqrY5tjlbZ4/s1600/specbyexample.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-ipgBb6TIiJw/Ugd_l0Ci2GI/AAAAAAAAAIc/KqrY5tjlbZ4/s320/specbyexample.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;&lt;a href=&quot;http://pboop.wordpress.com/&quot; target=&quot;_blank&quot;&gt;Nicole&lt;/a&gt; introduced some of the concepts of specification by example using the hot dog point of sale that was also used for the architecture kata during last years SoCraTes. She described some of the experiences they had when introducing it in their company (which doesn't sell hot dogs as far as I know). Specification by Example and BDD had been huge topics during last years conference and I would really like to try it once and see if it really improves communication. The &lt;a href=&quot;http://xpdays.de/twiki/pub/XPDays2012/SpecificationByExample/Spec_by_Example.pdf&quot; target=&quot;_blank&quot;&gt;(german) slides&lt;/a&gt; she used as an introduction are also available online.&lt;/p&gt; &lt;h4&gt;Designing Functional Programs&lt;/h4&gt; &lt;p&gt;A few people gathered to discuss the implications functional programming has when designing an application, e.g. when to use higher level functions. Unfortunately nobody was around who had already implemented an application in a functional way. To get some experience we tried to do the &lt;a href=&quot;http://craftsmanship.sv.cmu.edu/katas/mars-rover-kata&quot; target=&quot;_blank&quot;&gt;Mars Rover kata&lt;/a&gt; in Java Script. The kata probably was not the ideal choice as it is rather stateful and therefore a more advanced problem.&lt;/p&gt; &lt;h4&gt;Productivity Porn&lt;/h4&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-yd0ShljOokw/Ugd_h-9PqBI/AAAAAAAAAIE/wajkpKOaXbY/s1600/productivityport.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-yd0ShljOokw/Ugd_h-9PqBI/AAAAAAAAAIE/wajkpKOaXbY/s320/productivityport.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;A discussion on self management and everything around it. People shared some of their practices and Jan showed his foldable Personal Kanban board. It's a fact that you can really spend a lot of time thinking about productivity which sometime is not the most productive thing. But Personal Kanban seems to help a lot of people so I am planning to read &lt;a href=&quot;https://leanpub.com/PkFlowNutshell&quot; target=&quot;_blank&quot;&gt;the recommended book about it&lt;/a&gt; and try it for myself.&lt;/p&gt; &lt;h3&gt;Day 2&lt;/h3&gt; &lt;p&gt;&lt;a href=&quot;https://twitter.com/rhosts/status/363577303567040513/photo/1&quot; target=&quot;_blank&quot;&gt;Agenda for day 2&lt;/a&gt;, again taken by Robert.&lt;/p&gt; &lt;h4&gt;VIM Show and Tell&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://sebastianbenz.de/&quot; target=&quot;_blank&quot;&gt;Sebastian&lt;/a&gt; proposed a session on VIM where everybody should show their favourite plugin or feature. I am still a novice VIM user so I mainly learned some of the basics. Quite some people there are using VIM for their development work, mostly with dynamic languages I guess. &lt;a href=&quot;http://www.infoq.com/presentations/Vim-From-Essentials-to-Mastery&quot; target=&quot;_blank&quot;&gt;This video on InfoQ has been recommended&lt;/a&gt; and after watching it, I am also recommending it here.&lt;/p&gt;   &lt;h4&gt;Monads&lt;/h4&gt; &lt;p&gt;Another talk by Nicole where she introduced Monads, starting with an example in Java and moving on to Haskell. I have a better idea now on what Monads could be but the concept still is too abstract for me.&lt;/p&gt; &lt;h4&gt;Async Patterns&lt;/h4&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-eoyCZZX7Xzg/Ugd_ir88dqI/AAAAAAAAAIM/DV-hhrgG-o8/s1600/async.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-eoyCZZX7Xzg/Ugd_ir88dqI/AAAAAAAAAIM/DV-hhrgG-o8/s320/async.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Sergey presented three alternative solutions for building concurrent solutions:&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;Actors, a core feature of Erlang and Akka&lt;/li&gt;  &lt;li&gt;Reactive Extensions&lt;/li&gt;&lt;li&gt;Communicating Sequential Processes as implemented in &lt;a href=&quot;http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html&quot; target=&quot;_blank&quot;&gt;Clojure&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;SOLID Principles&lt;/h4&gt; &lt;p&gt;Another session by Sebastian where we discussed the &lt;a href=&quot;http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod&quot; target=&quot;_blank&quot;&gt;SOLID principles&lt;/a&gt;, considered to be the basics of good object oriented design. It was interesting to see that though you think you know the concepts it is still difficult to define them. While looking at some examples it also became obvious that sometimes you might follow one principle while violating another. Unfortunately I couldn't stay for the second, practical part of the session.&lt;/p&gt; &lt;h4&gt;Quit your Job&lt;/h4&gt; &lt;p&gt;During the world cafe &lt;a href=&quot;http://danieltemme.blogspot.de/&quot; target=&quot;_blank&quot;&gt;Daniel Temme&lt;/a&gt; mentioned that he had given a &lt;a href=&quot;http://vimeo.com/44327015&quot; target=&quot;_blank&quot;&gt;talk last year on quitting your job&lt;/a&gt; and quit his job just again. He told me parts of the story in the evening but I was glad that he decided to give the talk again. Though it is rather provocative the story behind it is important and spans a lot of areas of your life: Sometimes you are caught in your habits and don't really notice that the right thing to do would be something else. &lt;a href=&quot;http://danieltemme.blogspot.de/2013/08/journeyman-weeks.html&quot; target=&quot;_blank&quot;&gt;Daniel is currently on a journey where he visits company and works for food and accomodation.&lt;/a&gt;&lt;/p&gt;   &lt;h3&gt;Last words&lt;/h3&gt; &lt;p&gt;SoCraTes was really awesome again. Thanks to all the organizers and participants that shared a lot. I'll definitely try to be there again next year.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>GETting Results from ElasticSearch</title>
   <link href="http://blog.florian-hopf.de/2013/07/getting-results-from-elasticsearch.html"/>
   <updated>2013-07-31T13:41:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/07/getting-results-from-elasticsearch</id>
   <content type="html">&lt;p&gt;ElasticSearch exposes most of its functionality via a RESTful API. When it comes to querying the data, you can either pass request parameters, e.g. the query string, or use the &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/query-dsl/&quot; target=&quot;_blank&quot;&gt;query DSL&lt;/a&gt; which structures the queries as JSON objects. For a talk I gave I used this example which executes a GET request with curl and passes the JSON query structure in the request body.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET 'http://localhost:9200/jug/talk/_search' -d '{&lt;br /&gt;    &quot;query&quot; : {&lt;br /&gt;        &quot;query_string&quot; : {&quot;query&quot; : &quot;suche&quot;} &lt;br /&gt;    },&lt;br /&gt;    &quot;facets&quot; : {&lt;br /&gt;        &quot;tags&quot; : {&lt;br /&gt;            &quot;terms&quot; : {&quot;field&quot; : &quot;speaker&quot;} &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;An alert listener(*) later told me that you can't use a request body with a GET request. While preparing the talk I also thought about this and only added it after testing it successfully. At least it is unusual and some software like proxy caches might not handle your request as intended. A lot of ElasticSearch examples I have seen are using POST requests instead but I think semantically requesting search results should be a GET.&lt;/p&gt; &lt;p&gt;The &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/api/search/request-body/&quot; target=&quot;_blank&quot;&gt;ElasticSearch docs&lt;/a&gt; explicitly allow the use of GET requests with a request body:&lt;/p&gt; &lt;blockquote&gt;&quot;Both HTTP GET and HTTP POST can be used to execute search with body. Since not all clients support GET with body, POST is allowed as well.&quot;&lt;/blockquote&gt; &lt;p&gt;I didn't find a hint in the &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616.html&quot; target=&quot;_blank&quot;&gt;HTTP specification&lt;/a&gt; whether this should be allowed or not. &lt;a href=&quot;http://stackoverflow.com/a/978094/639892&quot; target=&quot;_blank&quot;&gt;This answer on Stackoverflow&lt;/a&gt; goes in the same direction as my initial concern, that you shouldn't do it because it might not be expected by users and also not supported by some stacks.&lt;/p&gt; &lt;p&gt;Finally, in &lt;a href=&quot;http://tech.groups.yahoo.com/group/rest-discuss/message/9962&quot; target=&quot;_blank&quot;&gt;this message&lt;/a&gt; Roy Fielding, one of the authors of the HTTP specification, discourages the use of a request body with GET.&lt;/p&gt; &lt;blockquote&gt;&quot;... any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request.&quot;&lt;/blockquote&gt; &lt;p&gt;As the query DSL influences the response it is clear that this is a semantic meaning, which by the words of Roy Fielding shouldn't be the done. So no consensus exists on this topic. By hindsight I am quite surprised that this works out that well with ElasticSearch and there aren't any problems I have heard about.&lt;/p&gt; &lt;p&gt;&lt;i&gt;(*) I wish he didn't tell me, while talking about another talk, that he always looks for mistakes in slides when he's bored ;).&lt;/i&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Book Review: Hibernate Search by Example</title>
   <link href="http://blog.florian-hopf.de/2013/06/book-review-hibernate-search-by-example.html"/>
   <updated>2013-06-05T17:58:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/06/book-review-hibernate-search-by-example</id>
   <content type="html">&lt;p&gt;&lt;i&gt;PacktPub kindly offered me a free review edition of &lt;a target=&quot;_blank&quot; href=&quot;http://www.packtpub.com/hibernate-search-by-example/book&quot;&gt;Hibernate Search by Example&lt;/a&gt;. Though I've used Lucene and Hibernate independently on a lot of projects I've never used &lt;a href=&quot;http://www.hibernate.org/subprojects/search.html&quot; target=&quot;_blank&quot;&gt;Hibernate Search&lt;/a&gt; which builds on both technologies. That makes me a good candidate for reviewing an introductory book on it.&lt;/i&gt;&lt;/p&gt; &lt;h4&gt;The Project&lt;/h4&gt; &lt;p&gt;Hibernate Search is a really interesting technology. It transparently indexes Hibernate entities in Lucene and provides a slick DSL for querying the index. You decide which entities and which fields to index by adding annotations on class and field level. Custom analyzer chains can be defined for the entities and referenced from fields. Each entity is written to its own index but it can also include data from related or embedded entities. By default, Lucene is only used for querying and ranking, the result list is still populated from the database. If this is not enough for your application you can also use projection to use stored fields in Lucene for result display.&lt;/p&gt; &lt;div style=&quot;clear: both; text-align: center;&quot; class=&quot;separator&quot;&gt;&lt;a style=&quot;clear:left; float:left;margin-right:1em; margin-bottom:1em&quot; href=&quot;http://www.packtpub.com/hibernate-search-by-example/book&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-adkfN2ImiU0/Ua8Ju5av7kI/AAAAAAAAAHg/JGuiwYrGPlo/s320/hibernate-search.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;The Book&lt;/h4&gt; &lt;p&gt;Steve Perkins, the author of Hibernate Search by Example, did a great job in designing an example that can evolve with the book. It starts very simple, also explaining the build setup using Maven and an embedded Jetty instance with a H2 database. Each following chapter builds on the results of the previous chapter and enhances the project with different features. Each chapter is dedicated to a certain topic that is immediately used in the application so you can see its benefit. This way you are learning different aspects, from mapping entities and performing queries to advanced mapping aspects, analyzing, filtering, using facets and even setting up master slave systems and sharding your data. But not only is the book structured in a good way, the author also has a very clear writing. Combined with the practical examples this makes it very easy to read. If you're planning to implement a solution on top of Hibernate Search you're well advised to read this book.&lt;/p&gt; &lt;h4&gt;My Impression of the Technology&lt;/h4&gt; &lt;p&gt;As I am building quite a lot of search applications I'd like to add some impressions of Hibernate Search. Though it's a very interesting technology I think you should be careful when deciding on its use. Not only will you tie yourself to Hibernate as your JPA provider but there are also some implications on the search side. Hibernate Search offers advanced features like faceting, it can be distributed and sharded. But there might be features that you want to build later on that would be far more easy with a search server like Solr or ElasticSearch. Hibernate Search uses some components of Solr but a real integration (using Solr as a backend) is rather difficult I guess. Solr needs in schema configured in a file so you would need to duplicate it. ElasticSearch could be a far better candidate as its schema mapping can be created with its REST API. I am really curious if somebody has been thinking about starting an implementation of Hibernate Search on top of ElasticSearch. With the Lucene implementation that is described in the book you can easily enhance your database driven application with advanced search functionality. But be aware that future requirements might be more difficult to build compared to integrating a search server from the beginning.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Getting Started with ElasticSearch: Part 1 - Indexing</title>
   <link href="http://blog.florian-hopf.de/2013/05/getting-started-with-elasticsearch-part.html"/>
   <updated>2013-05-28T14:44:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/05/getting-started-with-elasticsearch-part</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://elasticsearch.org&quot; target=&quot;blank&quot;&gt;ElasticSearch&lt;/a&gt; is gaining a huge momentum with large installations like Github and Stackoverflow switching to it for its search capabilities. Its distributed nature makes it an excellent choice for large datasets with high availability requirements. In this 2 part article I'd like to share what I learned building a small Java application just for search.&lt;/p&gt; &lt;p&gt;The example I am showing here is part of an application I am using for talks to show the capabilities of Lucene, Solr and ElasticSearch. It's a simple webapp that can search on user group talks. You can find the sources on &lt;a href=&quot;https://github.com/fhopf/lucene-solr-talk&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;   &lt;p&gt;Some experience with Solr can be helpful when starting with ElasticSearch but there are also times when it's best to not stick to your old knowledge.&lt;/p&gt; &lt;h4&gt;Installing ElasticSearch&lt;/h4&gt; &lt;p&gt;There is no real installation process involved when starting with ElasticSearch. It's only a Jar file that can be started immediately, either directly using the java command or via the shell scripts that are included with the binary distribution. You can pass the location of the configuration files and the index data using environment variables. This is a Gradle snippet I am using to start an ElasticSearch instance:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;task runES(type: JavaExec) {&lt;br /&gt;    main = 'org.elasticsearch.bootstrap.ElasticSearch'&lt;br /&gt;    classpath = sourceSets.main.runtimeClasspath&lt;br /&gt;    systemProperties = [&quot;es.path.home&quot;:'' + projectDir + '/elastichome',&lt;br /&gt;                        &quot;es.path.data&quot;:'' + projectDir + '/elastichome/data']&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You might expect that ElasticSearch uses a bundled Jetty instance as it has become rather common nowadays. But no, it implements all the transport layer with the asynchronous networking library &lt;a href=&quot;http://netty.io/&quot; target=&quot;_blank&quot;&gt;Netty&lt;/a&gt; so you never deploy it to a Servlet container.&lt;/p&gt; &lt;p&gt;After you started ElasticSearch it will be available at http://localhost:9200. Any further instances that you are starting will automatically connect to the existing cluster and even use another port automatically so there is no need for configuration and you won't see any &quot;Address already in use&quot; problems.&lt;/p&gt; &lt;p&gt;You can check that your installation works using some curl commands. &lt;/p&gt; &lt;p&gt;Index some data:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XPOST 'http://localhost:9200/jug/talk/' -d '{&lt;br /&gt;    &quot;speaker&quot; : &quot;Alexander Reelsen&quot;,&lt;br /&gt;    &quot;date&quot; : &quot;2013-06-12T19:15:00&quot;,&lt;br /&gt;    &quot;title&quot; : &quot;Elasticsearch&quot;&lt;br /&gt;}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;And search it:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl -XGET 'http://localhost:9200/jug/talk/_search?q=elasticsearch'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The url contains two fragments that determine the index name (jug) and the type (talk). You can have multiple indices per ElasticSearch instance and multiple types per index. Each type has its own mapping (schema) but you can also search across multiple types and multiple indices. Note that we didn't create the index and the type, ElasticSearch figures out index name and mapping automatically from the url and the structure of the indexed data.&lt;/p&gt; &lt;h4&gt;Java Client&lt;/h4&gt; &lt;p&gt;There are several alternative clients available when working with ElasticSearch from Java, like &lt;a href=&quot;https://github.com/searchbox-io/Jest&quot; target=&quot;_blank&quot;&gt;Jest&lt;/a&gt; that provides a POJO marshalling mechanism on indexing and for the search results. In this example we are using the Client that is included in ElasticSearch. By default the client doesn't use the REST API but connects to the cluster as a normal node that just doesn't store any data. It knows about the state of the cluster and can route requests to the correct node but supposedly consumes more memory. For our application this doesn't make a huge difference but for production systems that's something to think about. &lt;/p&gt; &lt;p&gt;This is an example setup for a Client object that can then be used for indexing and searching:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;Client client = NodeBuilder.nodeBuilder().client(true).node().client();&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can use the client to create an index:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;client.admin().indices().prepareCreate(INDEX).execute().actionGet();&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Note that the actionGet() isn't named this way because it is an HTTP GET request, this is a call to the Future object that is returned by execute, so this is the blocking part of the call.&lt;/p&gt; &lt;h4&gt;Mapping&lt;/h4&gt; &lt;p&gt;As you have seen with the indexing operation above ElasticSearch doesn't require an explicit schema like Solr does. It automatically determines the likely types from the JSON you are sending to it. Of course, this might not always be correct, and you might want to define custom analyzers for your content so you can also adjust the mappings to your needs. As I was so used to the way Solr does this that I was looking for a way to add the mapping configuration via a file in the server config. This is something you can do indeed using a file called default-mapping.json or via &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/api/admin-indices-templates/&quot; target=&quot;_blank&quot;&gt;index templates&lt;/a&gt;. On the other hand you can also use the REST based &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/api/admin-indices-put-mapping/&quot; target=&quot;_blank&quot;&gt;put mapping API&lt;/a&gt; which has the benefit that you don't need to distribute the file to all nodes manually and also you don't need to restart the server. The mapping  then is part of the cluster state and will get distributed to all nodes automatically.&lt;/p&gt; &lt;p&gt;ElasticSearch provides most of its API via Builder classes. Surprisingly I didn't find a Builder for the mapping. One way to construct it is to use the generic JSON builder:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;XContentBuilder builder = XContentFactory.jsonBuilder().&lt;br /&gt;  startObject().&lt;br /&gt;    startObject(TYPE).&lt;br /&gt;      startObject(&quot;properties&quot;).&lt;br /&gt;        startObject(&quot;path&quot;).&lt;br /&gt;          field(&quot;type&quot;, &quot;string&quot;).field(&quot;store&quot;, &quot;yes&quot;).field(&quot;index&quot;, &quot;not_analyzed&quot;).&lt;br /&gt;        endObject().&lt;br /&gt;        startObject(&quot;title&quot;).&lt;br /&gt;          field(&quot;type&quot;, &quot;string&quot;).field(&quot;store&quot;, &quot;yes&quot;).field(&quot;analyzer&quot;, &quot;german&quot;).&lt;br /&gt;        endObject().&lt;br /&gt;        // more mapping&lt;br /&gt;      endObject().&lt;br /&gt;    endObject().&lt;br /&gt;  endObject();&lt;br /&gt;client.admin().indices().preparePutMapping(INDEX).setType(TYPE).setSource(builder).execute().actionGet();&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Another way I have seen is to put the mapping in a file and just read it to a String, e.g. by using the &lt;a href=&quot;http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Resources.html&quot; target=&quot;_blank&quot;&gt;Guava Resources class&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;After you have adjusted the mapping you can have a look at the result at the _mapping endpoint of the index at http://localhost:9200/jug/_mapping?pretty=true.&lt;/p&gt; &lt;h4&gt;Indexing&lt;/h4&gt; &lt;p&gt;Now we are ready to index some data. In the example application I am using simple data classes that represent talks to be indexed. Again, you have different options how to transform your objects to the JSON ElasticSearch understands. You can build it by hand, e.g. with the XContentBuilder we have already seen above, or more conveniently, by using something like the JSON processor &lt;a href=&quot;http://jackson.codehaus.org/&quot; target=&quot;_blank&quot;&gt;Jackson&lt;/a&gt; that can serialize and deserialize Java objects to and from JSON. This is what it looks like when using the XContentBuilder:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;XContentBuilder sourceBuilder = XContentFactory.jsonBuilder().startObject()&lt;br /&gt;  .field(&quot;path&quot;, talk.path)&lt;br /&gt;  .field(&quot;title&quot;, talk.title)&lt;br /&gt;  .field(&quot;date&quot;, talk.date)&lt;br /&gt;  .field(&quot;content&quot;, talk.content)&lt;br /&gt;  .array(&quot;category&quot;, talk.categories.toArray(new String[0]))&lt;br /&gt;  .array(&quot;speaker&quot;, talk.speakers.toArray(new String[0]));&lt;br /&gt;IndexRequest request = new IndexRequest(INDEX, TYPE).id(talk.path).source(sourceBuilder);&lt;br /&gt;client.index(request).actionGet();&lt;/pre&gt;&lt;/code&gt; &lt;p&gt;You can also use the &lt;a href=&quot;http://www.elasticsearch.org/guide/reference/java-api/bulk/&quot; target=&quot;_blank&quot;&gt;BulkRequest&lt;/a&gt; to prevent having to send a request for each document.&lt;/p&gt; &lt;p&gt;With ElasticSearch you don't need to commit after you indexed. By default, it will refresh the index every second which is fast enough for most use cases. If you want to be able to search the data as soon as possible you can also call refresh() on the client. This can be really useful when writing tests and you don't want to wait for a second between indexing and searching.&lt;/p&gt; &lt;p&gt;This concludes the first part of this article on getting started with ElasticSearch using Java. &lt;a href=&quot;http://blog.florian-hopf.de/2013/08/getting-started-with-elasticsearch-part.html&quot;&gt;The second part contains more information on searching the data we indexed.&lt;/a&gt;&lt;/p&gt;  </content>
 </entry>
 
 <entry>
   <title>Softwerkskammer Rhein-Main Open Space</title>
   <link href="http://blog.florian-hopf.de/2013/02/softwerkskammer-rhein-main-open-space.html"/>
   <updated>2013-02-19T16:39:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/02/softwerkskammer-rhein-main-open-space</id>
   <content type="html">&lt;p&gt;On Saturday I attended an &lt;a href=&quot;http://rhein-main-openspace.softwerkskammer.de/&quot; target=&quot;_blank&quot;&gt;Open Space in Wiesbaden&lt;/a&gt;, organized by members of Softwerkskammer Rhein-Main, a very active chapter of the German software craftmanship community. The event took place in the offices of &lt;a href=&quot;http://www.seibert-media.net/&quot; target=&quot;_blank&quot;&gt;Seibert Media&lt;/a&gt; above a shopping mall including a nice view of the city.&lt;/p&gt; &lt;h4&gt;The Format&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Open-space_meeting&quot; target=&quot;_blank&quot;&gt;Open Space conferences&lt;/a&gt; are special as there is no predefined agenda. All the attendees can bring ideas and propose those in the opening session and choose a time slot and room. Sessions are not necessarily normal presentations but rather discussions so it's even OK to just propose a question that you have or a topic you'd like to learn more about from the attendees. Also, there are some guidelines and rules: sessions don't need to start and end in time, you can always leave a session in case you feel you can't contribute and you shouldn't be disappointed if nobody shows up for your proposed session.&lt;/p&gt; &lt;h4&gt;Personal Kanban&lt;/h4&gt; &lt;p&gt;Dennis Traub presented a session on &lt;a href=&quot;http://www.personalkanban.com/pk/&quot; target=&quot;_blank&quot;&gt;Personal Kanban&lt;/a&gt;. As I did Kanban style development in one project already I was eager to learn how to apply the principles to personal organization. Basically it all works the same as normal Kanban. Tasks are visualized on a board where a swimlane defines the state of a task with work items flowing from left (todo) to right (done). You can define swimlanes as it fits your habits, e.g. one for todos, one for in progress and one for blocked. The in progress lane needs to have a Work in Progress limit which is the amount of tasks you start and process in parallel. An important aspect is that you don't have to put all your backlog items to the todo lane but you can also keep them in a seperate place. This keeps you from getting overwhelmed when looking at the board.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-nnBQsluqZeM/USMe5pA2fhI/AAAAAAAAAHI/WaRA1hgL1uA/s1600/2013-02-16+12.15.04.jpg&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-nnBQsluqZeM/USMe5pA2fhI/AAAAAAAAAHI/WaRA1hgL1uA/s320/2013-02-16+12.15.04.jpg&quot; vertical-align=&quot;middle&quot;/&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;It sounds like Kanban is a good way for organizing your daily life. For me personally the biggest hindrance is that I am working from my living room and I'd rather not put a Kanban board in my living room. If I'd use a separate office I guess I'd try it immediately.&lt;/p&gt; &lt;h4&gt;Open Source&lt;/h4&gt; &lt;p&gt;An attendee wanted to know some experiences with Open Source communities. Two full time committers, &lt;a href=&quot;http://www.olivergierke.de/&quot; target=&quot;_blank&quot;&gt;Ollie&lt;/a&gt; for Spring and Marcel for Eclipse, shared some of their experiences. I am still surprised that a lot of Open Source projects have quite some bugs in their trackers that could easily be fixed by newcomers. A lot of people like Open Source software but not that many seem to be interested in contributing to a project continuously. Most of the interaction with users in the issue trackers are one time reports, so the people report one bug and move on. Even for big projects like Spring and Eclipse it's hard to find committers. One way to motivate people is to organize hack days where users learn to work with the sources of the projects but this also needs quite some preparation.&lt;/p&gt; &lt;h4&gt;Freelancing&lt;/h4&gt; &lt;p&gt;The topic of freelancing was discussed all over the day. &lt;a href=&quot;http://tckr.cc/&quot; target=&quot;_blank&quot;&gt;Markus Tacker&lt;/a&gt; presented his idea of the kybernetic agency, a plan to form a freelance network with people who can work on projects together. We discussed benefits and possible problems, mainly of legal type. A quite inspiring session that also made me think about the difference of freelancing in the Java enterprise world compared to PHP development. Most of the freelancers I know would prefer not to work 5 days a week for one client exclusively but that is often a prerequisite for projects in the enterprise world.&lt;/p&gt; &lt;h4&gt;Learning&lt;/h4&gt; &lt;p&gt;Learning is a topic that is very important to me so I proposed a session on it. I already switched from 5 to 4 days the last months of my employment at synyx because I felt the need to invest more time in learning which is often not possible when working on client projects. Even now as a freelancer I keep one day for learning only. What works best for me is writing blogposts that contain some sample code. I can build something and when writing the post I make sure that I have a deep understanding of the topic I am writing about. Other people also said that the most important aspect is to have something to work on, reading or watching screencasts alone is no sustainable activity. I also liked the technique of another freelancer: whenever he notices that he could do something different on the current project he stops to track the time for the customer and tries to find ways to improve the project, probably learning a new approach. This is something you are doing implicitly as a freelancer, you often spend some of your spare time thinking about client work but I like this explicit approach.&lt;/p&gt; &lt;h4&gt;Summary&lt;/h4&gt; &lt;p&gt;All in all this was a really fruitful, but also exhausting, day. Though I chose meta topics exclusively I gained a lot from visiting. Thanks a lot to the organizers (mainly &lt;a href=&quot;http://www.squeakyvessel.com/&quot; target=&quot;_blank&quot;&gt;Benjamin&lt;/a&gt;), moderators, sponsors and all the attendees that made this event possible. I am looking forward to meeting a lot of the people again at &lt;a href=&quot;http://www.socrates-conference.de/&quot; target=&quot;_blank&quot;&gt;Socrates&lt;/a&gt; this year.&lt;p/&gt;</content>
 </entry>
 
 <entry>
   <title>Book Review:  Gradle Effective Implementation Guide</title>
   <link href="http://blog.florian-hopf.de/2013/02/book-review-gradle-effective.html"/>
   <updated>2013-02-01T19:10:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/02/book-review-gradle-effective</id>
   <content type="html">&lt;i&gt;PacktPub kindly offered me a free review edition of &lt;a target=&quot;_blank&quot; href=&quot;http://www.packtpub.com/gradle-effective-implementation-guide/book&quot;&gt;Gradle Effective Implementation Guide&lt;/a&gt; written by &lt;a target=&quot;_blank&quot; href=&quot;http://mrhaki.blogspot.de/&quot;&gt;mrhaki Hubert Klein Ikkink&lt;/a&gt;. As I planned to read it anyway I agreed to write a review of it.&lt;/i&gt; &lt;p&gt;Maven was huge for Java Development. It brought dependency management, sane conventions and platform independent builds to the mainstream. If there is a Maven pom file available for an open source project you can be quite sure to manage to build it on your local machine in no time.&lt;/p&gt; &lt;p&gt;But there are cases when it doesn't work that well. Its phase model is rather strict and the one-artifact-per-build restriction can get in your way for more unusual build setups. You can workaround some of these problems using profiles and assemblies but it feels that it is primarily useful for a certain set of projects.&lt;/p&gt; &lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;http://www.gradle.org/&quot;&gt;Gradle&lt;/a&gt; is different. It's more flexible but there's also a learning curve involved. Groovy as its build DSL is easy to read but probably not that easy to write at first because there are often multiple ways to do something. As a standard Java developer like me you might be unsure about the proper way of doing something.&lt;/p&gt; &lt;p&gt;There are a lot of helpful resources online, namely the &lt;a target=&quot;_blank&quot; href=&quot;http://forums.gradle.org/gradle&quot;&gt;forum&lt;/a&gt; and the &lt;a target=&quot;_blank&quot; href=&quot;http://www.gradle.org/docs/current/userguide/userguide.html&quot;&gt;excellent user guide&lt;/a&gt; but as I prefer to read longer sections offline I am really glad that there now is a book available that contains extensive information and can get you started with Gradle.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a target=&quot;_blank&quot; href=&quot;http://www.packtpub.com/gradle-effective-implementation-guide/book&quot; style=&quot;clear:left; float:left;margin-right:1em; margin-bottom:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; width=&quot;259&quot; src=&quot;http://1.bp.blogspot.com/-e8x0L35XNKc/UQuc7l1B__I/AAAAAAAAAG4/wwbHwriWSeU/s320/gradle-effective-implementation-guide.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Content&lt;/h4&gt; &lt;p&gt;The book starts with a general introduction into Gradle. You'll get a high level overview of its features, learn how to install it and write your first build file. You'll also learn some important options of the gradle executable that I haven't been aware of.&lt;/p&gt; &lt;p&gt;Chapter 2 explains tasks and how to write build files. This is a very important chapter if you are not that deep into the Groovy language. You'll learn about the implicitly available Task and Project instances and the different ways of accessing methods and properties and of defining tasks and dependencies between them.&lt;/p&gt; &lt;p&gt;Working with files is an important part of any build system. Chapter 3 contains detailed information on accessing and modifying files, file collections and file trees. This is also where the benefit of using Groovy becomes really obvious. The ease of working with collections can lead to very concise build definitions though you have all the power of Groovy and the JVM at your hands. The different log levels are useful to know and can come in handy when you'd like to diagnose a build.&lt;/p&gt; &lt;p&gt;While understanding tasks is an important foundation for working with Gradle it's likely that you are after using it with programming languages. Nearly all of the remaining chapters cover working with different aspects on builds for JVM languages. Chapter 4 starts with a look at the Java plugin and its additional concepts. You'll see how you can compile and package Java applications and how to work with sourceSets.&lt;/p&gt; &lt;p&gt;Nearly no application is an island. The Java world provides masses of useful libraries that can help you build your application. Proper dependency management, as introduced in Chapter 5, is important for easy build setups and for making sure that you do not introduce incompatible combinations of libraries. Gradle supports Maven, Ivy and local file based repositories. Configurations are used to group dependencies, e.g. to define dependencies that are only necessary for tests. If you need to influence the version you are retrieving for a certain dependency you can configure resolution strategies, version ranges and exclusions for transitive dependencies.&lt;/p&gt; &lt;p&gt;Automated testing is a crucial part of any modern software development process. Gradle can work with JUnit and TestNG out of the box. Test execution times can be improved a lot by the incremental build support and the parallelization of tests. I guess this can lead to dramatically shorter build times, something I plan to try on an example project with a lot of tests in the near future. This chapter also introduces the different ways to run an application, create distributions and how to publish artifacts.&lt;/p&gt; &lt;p&gt;The next chapter will show you how you can structure your application in separate projects. Gradle has clever ways to find out which projects need to be rebuild before and after building a certain project.&lt;/p&gt; &lt;p&gt;Chapter 8 contains information on how to work with Scala and Groovy code. The necessary compiler versions can be defined in the build so there is no need to have additional installations. I've heard good things about the Scala integration so Gradle seems to be a viable alternative to sbt.&lt;/p&gt; &lt;p&gt;The check task can be used to gather metrics on your project using many of the available open source projects for code quality measurement. Chapter 9 shows you how to include tools like Checkstyle, PMD and FindBugs to analyze your project sources, either standalone or by sending data to Sonar.&lt;/p&gt; &lt;p&gt;If you need additional functionality that is not available you can start implementing your own tasks and plugins. Chapter 10 introduces the important classes for writing custom plugins and how to use them from Groovy and Java.&lt;/p&gt; &lt;p&gt;Gradle can be used on several Continuous Integration systems. As I've been working with Hudson/Jenkins exclusively during the last years it was interesting to also read about the commercial alternatives Team City and Bamboo in Chapter 11.&lt;/p&gt; &lt;p&gt;The final chapter contains a lot of in depth information on the Eclipse and IDEA plugins. Honestly, this contains more information on the Eclipse file format than I wanted to know but I guess that can be really useful for users. Unfortunately the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/kelemen/netbeans-gradle-project&quot;&gt;excellent Netbeans plugin&lt;/a&gt; is not described in the book.&lt;/p&gt; &lt;h4&gt;Summary&lt;/h4&gt; &lt;p&gt;The book is an excellent introduction into working effectively with Gradle. It has helped me to get a far better understanding of the concepts. If you are thinking about or already started working with Gradle I highly recommend to &lt;a target=&quot;_blank&quot; href=&quot;http://www.packtpub.com/gradle-effective-implementation-guide/book&quot;&gt;get a copy&lt;/a&gt;. There are a lot of detailed example files that you can use immediately. Many of those are very close to real world use cases and can help you thinking about additional ways Gradle can be useful for organizing your builds.&lt;/p&gt; </content>
 </entry>
 
 <entry>
   <title>Make your Filters Match: Faceting in Solr</title>
   <link href="http://blog.florian-hopf.de/2013/01/make-your-filters-match-faceting-in-solr.html"/>
   <updated>2013-01-24T16:49:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/01/make-your-filters-match-faceting-in-solr</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://searchhub.org/2009/09/02/faceted-search-with-solr/&quot; target=&quot;_blank&quot;&gt;Facets&lt;/a&gt; are a great search feature that let users easily navigate to the documents they are looking for. Solr makes it really easy to use them though when naively querying for facet values you might see some unexpected behaviour. Read on to learn the basics of what is happening when you are passing in filter queries for faceting. Also, I'll show how you can leverage local params to choose a different query parser when selecting facet values.&lt;/p&gt; &lt;h4&gt;Introduction&lt;/h4&gt; &lt;p&gt;Facets are a way to display categories next to a users search results, often with a count of how many results are in this category. The user can then select one of those facet values to retrieve only those results that are assigned to this category. This way he doesn't have to know what category he is looking for when entering the search term as all the available categories are delivered with the search results. This approach is really popular on sites like Amazon and eBay and is a great way to guide the user.&lt;/p&gt; &lt;p&gt;Solr brought faceting to the Lucene world and arguably the feature was an important driving factor for its success (&lt;a href=&quot;http://shaierera.blogspot.com/2012/11/lucene-facets-part-1.html&quot; target=&quot;_blank&quot;&gt;Lucene 3.4 introduced faceting as well&lt;/a&gt;). Facets can be build from terms in the index, custom queries and ranges though in this post we will only look at field facets.&lt;/p&gt;   &lt;p&gt;As a very simple example consider this schema definition:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;fields&amp;gt;&lt;br/&gt;    &amp;lt;field name=&amp;quot;id&amp;quot; type=&amp;quot;string&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;true&amp;quot; required=&amp;quot;true&amp;quot; multiValued=&amp;quot;false&amp;quot; /&amp;gt; &lt;br/&gt;    &amp;lt;field name=&amp;quot;text&amp;quot; type=&amp;quot;text_general&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;    &amp;lt;field name=&amp;quot;author&amp;quot; type=&amp;quot;string&amp;quot; indexed=&amp;quot;true&amp;quot; stored=&amp;quot;false&amp;quot;/&amp;gt;&lt;br/&gt;&amp;lt;/fields&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;There are three fields, the id, a title that we'd probably like to search on and an author. The author is defined as a string field which means no analyzing at all. The faceting mechanism uses the term value and not a stored value so we want to make sure that the original value is preserved. I explicitly don't store the author information to make it clear that we are working with the indexed value.&lt;/p&gt; &lt;p&gt;Let's index some book data with curl (see &lt;a href=&quot;https://github.com/fhopf/solr-facet-example&quot; target=&quot;_blank&quot;&gt;this GitHub repo&lt;/a&gt; for the complete example including some unit tests that execute the same functionality using Java).&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl http://localhost:8082/solr/update -H &amp;quot;Content-Type: text/xml&amp;quot; --data-binary \&lt;br/&gt;    '&amp;lt;add&amp;gt;&amp;lt;doc&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;id&amp;quot;&amp;gt;1&amp;lt;/field&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;text&amp;quot;&amp;gt;On the Shortness of Life&amp;lt;/field&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;author&amp;quot;&amp;gt;Seneca&amp;lt;/field&amp;gt;&lt;br/&gt;    &amp;lt;/doc&amp;gt; &lt;br/&gt;    &amp;lt;doc&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;id&amp;quot;&amp;gt;2&amp;lt;/field&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;text&amp;quot;&amp;gt;What I Talk About When I Talk About Running&amp;lt;/field&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;author&amp;quot;&amp;gt;Haruki Murakami&amp;lt;/field&amp;gt;&lt;br/&gt;    &amp;lt;/doc&amp;gt; &lt;br/&gt;    &amp;lt;doc&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;id&amp;quot;&amp;gt;3&amp;lt;/field&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;text&amp;quot;&amp;gt;The Dude and the Zen Master&amp;lt;/field&amp;gt;&lt;br/&gt;            &amp;lt;field name=&amp;quot;author&amp;quot;&amp;gt;Jeff &amp;quot;The Dude&amp;quot; Bridges&amp;lt;/field&amp;gt;&lt;br/&gt;    &amp;lt;/doc&amp;gt;&lt;br/&gt;    &amp;lt;/add&amp;gt;'&lt;br/&gt;curl http://localhost:8082/solr/update -H &amp;quot;Content-Type: text/xml&amp;quot; --data-binary '&amp;lt;commit /&amp;gt;'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;And verify that the documents are available:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl http://localhost:8082/solr/query?q=*:*&lt;br/&gt;{&lt;br/&gt;  &amp;quot;responseHeader&amp;quot;:{&lt;br/&gt;    &amp;quot;status&amp;quot;:0,&lt;br/&gt;    &amp;quot;QTime&amp;quot;:3,&lt;br/&gt;    &amp;quot;params&amp;quot;:{&lt;br/&gt;      &amp;quot;q&amp;quot;:&amp;quot;*:*&amp;quot;}},&lt;br/&gt;  &amp;quot;response&amp;quot;:{&amp;quot;numFound&amp;quot;:3,&amp;quot;start&amp;quot;:0,&amp;quot;docs&amp;quot;:[&lt;br/&gt;      {&lt;br/&gt;        &amp;quot;id&amp;quot;:&amp;quot;1&amp;quot;,&lt;br/&gt;        &amp;quot;text&amp;quot;:&amp;quot;On the Shortness of Life&amp;quot;},&lt;br/&gt;      {&lt;br/&gt;        &amp;quot;id&amp;quot;:&amp;quot;2&amp;quot;,&lt;br/&gt;        &amp;quot;text&amp;quot;:&amp;quot;What I Talk About When I Talk About Running&amp;quot;},&lt;br/&gt;      {&lt;br/&gt;        &amp;quot;id&amp;quot;:&amp;quot;3&amp;quot;,&lt;br/&gt;        &amp;quot;text&amp;quot;:&amp;quot;The Dude and the Zen Master&amp;quot;}]&lt;br/&gt;  }}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;I'll omit parts of the response in the following examples. We can also have a look at the shiny new administration view of Solr 4 to see all terms that are indexed for the field author.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-Gyieq4YvHEo/UQDpZCNB82I/AAAAAAAAAGk/AVL8zIszDzE/s1600/author-fields.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;238&quot; width=&quot;320&quot; src=&quot;http://4.bp.blogspot.com/-Gyieq4YvHEo/UQDpZCNB82I/AAAAAAAAAGk/AVL8zIszDzE/s320/author-fields.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Each of the author names is indexed as one term.&lt;/p&gt; &lt;h4&gt;Faceting&lt;/h4&gt; &lt;p&gt;Let's move on to the faceting part. To let the user drill down on search results there are two steps involved. First you tell Solr that you would like to retrieve facets with the results. Facets are contained in an extra section of the response and consist of the indexed term as well as a count. As with most Solr parameters you can either send the necessary options with the query or preconfigure them in solrconfig.xml. This query has faceting on the author field enabled:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl &amp;quot;http://localhost:8082/solr/query?q=*:*&amp;facet=on&amp;facet.field=author&amp;quot;&lt;br /&gt;{&lt;br /&gt;  &amp;quot;responseHeader&amp;quot;:{...},&lt;br /&gt;  &amp;quot;response&amp;quot;:{&amp;quot;numFound&amp;quot;:3,&amp;quot;start&amp;quot;:0,&amp;quot;docs&amp;quot;:[&lt;br /&gt;      {&lt;br /&gt;        &amp;quot;id&amp;quot;:&amp;quot;1&amp;quot;,&lt;br /&gt;        &amp;quot;text&amp;quot;:&amp;quot;On the Shortness of Life&amp;quot;},&lt;br /&gt;      {&lt;br /&gt;        &amp;quot;id&amp;quot;:&amp;quot;2&amp;quot;,&lt;br /&gt;        &amp;quot;text&amp;quot;:&amp;quot;What I Talk About When I Talk About Running&amp;quot;},&lt;br /&gt;      {&lt;br /&gt;        &amp;quot;id&amp;quot;:&amp;quot;3&amp;quot;,&lt;br /&gt;        &amp;quot;text&amp;quot;:&amp;quot;The Dude and the Zen Master&amp;quot;}]&lt;br /&gt;  },&lt;br /&gt;  &amp;quot;facet_counts&amp;quot;:{&lt;br /&gt;    &amp;quot;facet_queries&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_fields&amp;quot;:{&lt;br /&gt;      &amp;quot;author&amp;quot;:[&lt;br /&gt;        &amp;quot;Haruki Murakami&amp;quot;,1,&lt;br /&gt;        &amp;quot;Jeff \&amp;quot;The Dude\&amp;quot; Bridges&amp;quot;,1,&lt;br /&gt;        &amp;quot;Seneca&amp;quot;,1]},&lt;br /&gt;    &amp;quot;facet_dates&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_ranges&amp;quot;:{}}}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;And this is what a configuration in solrconfig looks like:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;requestHandler name=&amp;quot;/select&amp;quot; class=&amp;quot;solr.SearchHandler&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;lst name=&amp;quot;defaults&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;q&amp;quot;&amp;gt;*:*&amp;lt;/str&amp;gt;  &lt;br /&gt;    &amp;lt;str name=&amp;quot;echoParams&amp;quot;&amp;gt;none&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;int name=&amp;quot;rows&amp;quot;&amp;gt;10&amp;lt;/int&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;df&amp;quot;&amp;gt;text&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;facet&amp;quot;&amp;gt;on&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;facet.field&amp;quot;&amp;gt;author&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;facet.mincount&amp;quot;&amp;gt;1&amp;lt;/str&amp;gt;&lt;br /&gt;  &amp;lt;/lst&amp;gt;&lt;br /&gt;&amp;lt;/requestHandler&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This way we don't have to pass the parameters with the query anymore and can see which parts of the query change.&lt;/p&gt; &lt;h4&gt;Common Filtering&lt;/h4&gt; &lt;p&gt;When a user chooses a facet you issue the same query again, this time adding a filter query that restricts the search results to any that have the value for this certain fields set. In our case the user would only see books of one certain author. Let's start simple and pretend that a user can't handle the massive amount of 3 search results and is only interested in books on Seneca:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq=author:Seneca'&lt;br /&gt;{&lt;br /&gt;  &amp;quot;responseHeader&amp;quot;:{...},&lt;br /&gt;  &amp;quot;response&amp;quot;:{&amp;quot;numFound&amp;quot;:1,&amp;quot;start&amp;quot;:0,&amp;quot;docs&amp;quot;:[&lt;br /&gt;      {&lt;br /&gt;        &amp;quot;id&amp;quot;:&amp;quot;1&amp;quot;,&lt;br /&gt;        &amp;quot;text&amp;quot;:&amp;quot;On the Shortness of Life&amp;quot;}]&lt;br /&gt;  },&lt;br /&gt;  &amp;quot;facet_counts&amp;quot;:{&lt;br /&gt;    &amp;quot;facet_queries&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_fields&amp;quot;:{&lt;br /&gt;      &amp;quot;author&amp;quot;:[&lt;br /&gt;        &amp;quot;Seneca&amp;quot;,1]},&lt;br /&gt;    &amp;quot;facet_dates&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_ranges&amp;quot;:{}}}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Works fine. We added a filter query that restricts the results to only those that are written by Seneca. Note that there is only one facet left because the search results don't contain any books by other authors. Let's see what happens when we try to filter the results to see only books by Haruki Murakami. We need to URL encode the blank, the rest of the query stays the same:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq=author:Haruki%20Murakami'&lt;br /&gt;{&lt;br /&gt;  &amp;quot;responseHeader&amp;quot;:{...},&lt;br /&gt;  &amp;quot;response&amp;quot;:{&amp;quot;numFound&amp;quot;:0,&amp;quot;start&amp;quot;:0,&amp;quot;docs&amp;quot;:[]&lt;br /&gt;  },&lt;br /&gt;  &amp;quot;facet_counts&amp;quot;:{&lt;br /&gt;    &amp;quot;facet_queries&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_fields&amp;quot;:{&lt;br /&gt;      &amp;quot;author&amp;quot;:[]},&lt;br /&gt;    &amp;quot;facet_dates&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_ranges&amp;quot;:{}}}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;No results. Why is that? The default query parser for filter queries is the Lucene query parser. It tokenizes the query on whitespace, so even if we store the field unanalyzed it's not the query we are probably expecting to use. The query that is the result of the parsing process is not a term query as in our first example. It's a boolean query that consists of two term queries &lt;code&gt;author:Haruki text:murakami&lt;/code&gt;. If you are familiar with the &lt;a href=&quot;http://lucene.apache.org/core/4_0_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html&quot; target=&quot;_blank&quot;&gt;Lucene query syntax&lt;/a&gt; this won't be a surprise to you. If you prefix a term with a field name and a colon it will search on this field, otherwise it will search on the default field we declared in solrconfig.xml.&lt;/p&gt; &lt;p&gt;How can we fix it? Simple, just turn it into a phrase by surrounding the words with double quotes:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq=author:&amp;quot;Haruki%20Murakami&amp;quot;'&lt;br /&gt;{&lt;br /&gt;  &amp;quot;responseHeader&amp;quot;:{...},&lt;br /&gt;  &amp;quot;response&amp;quot;:{&amp;quot;numFound&amp;quot;:1,&amp;quot;start&amp;quot;:0,&amp;quot;docs&amp;quot;:[&lt;br /&gt;      {&lt;br /&gt;        &amp;quot;id&amp;quot;:&amp;quot;2&amp;quot;,&lt;br /&gt;        &amp;quot;text&amp;quot;:&amp;quot;What I Talk About When I Talk About Running&amp;quot;}]&lt;br /&gt;  },&lt;br /&gt;  &amp;quot;facet_counts&amp;quot;:{&lt;br /&gt;    &amp;quot;facet_queries&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_fields&amp;quot;:{&lt;br /&gt;      &amp;quot;author&amp;quot;:[&lt;br /&gt;        &amp;quot;Haruki Murakami&amp;quot;,1]},&lt;br /&gt;    &amp;quot;facet_dates&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_ranges&amp;quot;:{}}}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Or, if you prefer, you can also escape the blank using the backslash, which yields the same result:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq=author:Haruki\%20Murakami'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Fun fact: I am not that good at picking examples. If we are filtering on our last author we will be surprised (at least I scratched my head for a while):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq=author:Jeff%20&amp;quot;The%20Dude&amp;quot;%20Bridges'&lt;br /&gt;{&lt;br /&gt;  &amp;quot;responseHeader&amp;quot;:{...},&lt;br /&gt;  &amp;quot;response&amp;quot;:{&amp;quot;numFound&amp;quot;:1,&amp;quot;start&amp;quot;:0,&amp;quot;docs&amp;quot;:[&lt;br /&gt;      {&lt;br /&gt;        &amp;quot;id&amp;quot;:&amp;quot;3&amp;quot;,&lt;br /&gt;        &amp;quot;text&amp;quot;:&amp;quot;The Dude and the Zen Master&amp;quot;}]&lt;br /&gt;  },&lt;br /&gt;  &amp;quot;facet_counts&amp;quot;:{&lt;br /&gt;    &amp;quot;facet_queries&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_fields&amp;quot;:{&lt;br /&gt;      &amp;quot;author&amp;quot;:[&lt;br /&gt;        &amp;quot;Jeff \&amp;quot;The Dude\&amp;quot; Bridges&amp;quot;,1]},&lt;br /&gt;    &amp;quot;facet_dates&amp;quot;:{},&lt;br /&gt;    &amp;quot;facet_ranges&amp;quot;:{}}}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;This actually seemed to work though we neither turned it into a phrase nor did we escape the blanks. If we look at how the Lucene query parser handles this query we see immediately why this returns a result. As with the last example this is turned into a boolean query, only the first query is executed against the author field. The other two tokens are searching on the default field and in this case &quot;The Dude&quot; matches the text field: &lt;code&gt;author:Jeff text:&quot;the dude&quot; text:bridges&lt;/code&gt;. If you just want to match on the author field you can escape the blanks as we did in the example before:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq=author:Jeff\%20\&quot;The\%20Dude\&quot;\%20Bridges'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;I'll spare you with the response.&lt;/p&gt; &lt;h4&gt;Using Local Params to set the Query Parser&lt;/h4&gt; &lt;p&gt;At ApacheCon Europe in November Eric Hatcher did &lt;a href=&quot;http://archive.apachecon.com/eu2012/presentations/06-Tuesday/PR-Lucene/aceu-2012-query-parsing-tips-and-tricks.pdf&quot; target=&quot;_blank&quot;&gt;a really interesting presentation on query parsers in Solr&lt;/a&gt; where he introduced another, probably cleaner way to do this: You can use the &lt;a href=&quot;http://wiki.apache.org/solr/LocalParams&quot; target=&quot;_blank&quot;&gt;local param syntax&lt;/a&gt; for choosing a different query parser. As we have learnt, the query parser defaults to the Lucene query parser. You can change the query parser for the query by setting the defType parameter, either via request parameters or in the solrconfig.xml but I am not aware of any way to set it for the filter queries. As we have unanalyzed terms the correct thing to do would be to use a TermQuery, which can be built using the &lt;a href=&quot;http://lucene.apache.org/solr/4_1_0/solr-core/org/apache/solr/search/TermQParserPlugin.html&quot; target=&quot;_blank&quot;&gt;TermQParserPlugin&lt;/a&gt;. To use this parser we can explicitly set it in the filter query:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq={!term%20f=author%20v='Jeff%20&quot;The%20Dude&quot;%20Bridges'}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Or, for better readability, without the URL encoding:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/select?fq={!term f=author v='Jeff &quot;The Dude&quot; Bridges'}'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The local params are enclosed by curly braces. The value term is a shorthand for type='term', f is the fiels the TermQuery should be built for and v the value. Though this might look quirky at first this is a really powerful feature, especially since you can reference other request parameters from the local params. Consider this configuration of a request handler:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;requestHandler name=&amp;quot;/selectfiltered&amp;quot; class=&amp;quot;solr.SearchHandler&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;lst name=&amp;quot;defaults&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;q&amp;quot;&amp;gt;*:*&amp;lt;/str&amp;gt;  &lt;br /&gt;    &amp;lt;str name=&amp;quot;echoParams&amp;quot;&amp;gt;explicit&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;int name=&amp;quot;rows&amp;quot;&amp;gt;10&amp;lt;/int&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;wt&amp;quot;&amp;gt;json&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;indent&amp;quot;&amp;gt;true&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;df&amp;quot;&amp;gt;text&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;facet&amp;quot;&amp;gt;on&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;facet.field&amp;quot;&amp;gt;author&amp;lt;/str&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;facet.mincount&amp;quot;&amp;gt;1&amp;lt;/str&amp;gt;&lt;br /&gt;  &amp;lt;/lst&amp;gt;&lt;br /&gt;  &amp;lt;lst name=&amp;quot;appends&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;str name=&amp;quot;fq&amp;quot;&amp;gt;{!term f=author v=$author}&amp;lt;/str&amp;gt;&lt;br /&gt;  &amp;lt;/lst&amp;gt;&lt;br /&gt;&amp;lt;/requestHandler&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The default configuration is the same as we were using above. Only the appends section is new, which adds additional parameters to the request. There are similar local params as we were using via curl, but the real filter query is replaced by the variable $author. This can now be passed in cleanly via an aptly named parameter:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl 'http://localhost:8082/solr/selectfiltered?author=Jeff%20&quot;The%20Dude&quot;%20Bridges'&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;There are a lot of powerful features in Solr that are not that commonly used. To see this example in Java have a look at &lt;a href=&quot;https://github.com/fhopf/solr-facet-example&quot; target=&quot;_blank&quot;&gt;the Github repository of this blogpost&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>JUnit Rule for ElasticSearch</title>
   <link href="http://blog.florian-hopf.de/2013/01/junit-rule-for-elasticsearch.html"/>
   <updated>2013-01-10T15:03:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/01/junit-rule-for-elasticsearch</id>
   <content type="html">&lt;p&gt;While I am using &lt;a href=&quot;http://lucene.apache.org/solr/&quot; target=&quot;_blank&quot;&gt;Solr&lt;/a&gt; a lot in my current engagement I recently started a pet project with &lt;a href=&quot;http://www.elasticsearch.org/&quot; target=&quot;_blank&quot;&gt;ElasticSearch&lt;/a&gt; to learn more about it. Some of its functionality is rather different from Solr so there is quite some experimentation involved. I like to start small and implement tests if I like to find out how things work (see &lt;a href=&quot;http://blog.florian-hopf.de/2012/06/running-and-testing-solr-with-gradle.html&quot; target=&quot;_blank&quot;&gt;this post on how to write tests for Solr&lt;/a&gt;).&lt;/p&gt; &lt;p&gt;ElasticSearch internally uses &lt;a href=&quot;http://testng.org/doc/index.html&quot; target=&quot;_blank&quot;&gt;TestNG&lt;/a&gt; and the test classes are not available in the distributed jar files. Fortunately it is really easy to start an ElasticSearch instance from within a test so it's no problem to do something similar in JUnit. Felix Müller posted some &lt;a href=&quot;http://cupofjava.de/blog/2012/11/27/embedded-elasticsearch-server-for-tests/&quot; target=&quot;_blank&quot;&gt;useful code snippets&lt;/a&gt; on how to do this, obviously targeted at a Maven build. The ElasticSearch instance is started in a setUp method and stopped in a tearDown method:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;private EmbeddedElasticsearchServer embeddedElasticsearchServer;&lt;br /&gt;&lt;br /&gt;@Before&lt;br /&gt;public void startEmbeddedElasticsearchServer() {&lt;br /&gt;    embeddedElasticsearchServer = new EmbeddedElasticsearchServer();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@After&lt;br /&gt;public void shutdownEmbeddedElasticsearchServer() {&lt;br /&gt;    embeddedElasticsearchServer.shutdown();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;As it is rather cumbersome to add these methods to all tests I transformed the code to a &lt;a href=&quot;http://kentbeck.github.com/junit/javadoc/4.10/org/junit/rules/TestRule.html&quot; target=&quot;_blank&quot;&gt;JUnit rule&lt;/a&gt;. Rules can execute code before and after a test is run and influence its execution. There are some base classes available that make it really easy to get started with custom rules.&lt;/p&gt; &lt;p&gt;Our ElasticSearch example can be easily modeled using the base class &lt;a href=&quot;http://kentbeck.github.com/junit/javadoc/4.10/org/junit/rules/ExternalResource.html&quot; target=&quot;_blank&quot;&gt;ExternalResource&lt;/a&gt; (see the &lt;a href=&quot;https://github.com/fhopf/elasticsearch-junit-rule&quot; target=&quot;_blank&quot;&gt;full example code on GitHub&lt;/a&gt;):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public class ElasticsearchTestNode extends ExternalResource {&lt;br /&gt;&lt;br /&gt;    private Node node;&lt;br /&gt;    private Path dataDirectory;&lt;br /&gt;    &lt;br /&gt;    @Override&lt;br /&gt;    protected void before() throws Throwable {&lt;br /&gt;        try {&lt;br /&gt;            dataDirectory = Files.createTempDirectory(&quot;es-test&quot;, new FileAttribute&lt;?&gt; []{});&lt;br /&gt;        } catch (IOException ex) {&lt;br /&gt;            throw new IllegalStateException(ex);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        ImmutableSettings.Builder elasticsearchSettings = ImmutableSettings.settingsBuilder()&lt;br /&gt;                .put(&quot;http.enabled&quot;, &quot;false&quot;)&lt;br /&gt;                .put(&quot;path.data&quot;, dataDirectory.toString());&lt;br /&gt;&lt;br /&gt;        node = NodeBuilder.nodeBuilder()&lt;br /&gt;                .local(true)&lt;br /&gt;                .settings(elasticsearchSettings.build())&lt;br /&gt;                .node();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    protected void after() {&lt;br /&gt;        node.close();&lt;br /&gt;        try {&lt;br /&gt;            FileUtils.deleteDirectory(dataDirectory.toFile());&lt;br /&gt;        } catch (IOException ex) {&lt;br /&gt;            throw new IllegalStateException(ex);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public Client getClient() {&lt;br /&gt;        return node.client();&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The before method is executed before the test is run so we can use it to start ElasticSearch. All data is written to a temporary folder. The after method is used to stop ElasticSearch and delete the folder.&lt;/p&gt; &lt;p&gt;In your test you can now just use the rule, either with the @Rule annotation to have it triggered on each test method, or using @ClassRule to execute it only once per class:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public class CoreTest {&lt;br /&gt;&lt;br /&gt;    @Rule&lt;br /&gt;    public ElasticsearchTestNode testNode = new ElasticsearchTestNode();&lt;br /&gt;    &lt;br /&gt;    @Test&lt;br /&gt;    public void indexAndGet() throws IOException {&lt;br /&gt;        testNode.getClient().prepareIndex(&quot;myindex&quot;, &quot;document&quot;, &quot;1&quot;)&lt;br /&gt;                .setSource(jsonBuilder().startObject().field(&quot;test&quot;, &quot;123&quot;).endObject())&lt;br /&gt;                .execute()&lt;br /&gt;                .actionGet();&lt;br /&gt;        &lt;br /&gt;        GetResponse response = testNode.getClient().prepareGet(&quot;myindex&quot;, &quot;document&quot;, &quot;1&quot;).execute().actionGet();&lt;br /&gt;        assertThat(response.getSource().get(&quot;test&quot;)).isEqualTo(&quot;123&quot;);&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;As it is really easy to implement custom rules I think this is a feature I will be using more often in the future.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>12 Conferences of 2012</title>
   <link href="http://blog.florian-hopf.de/2013/01/12-conferences-of-2012.html"/>
   <updated>2013-01-03T17:52:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2013/01/12-conferences-of-2012</id>
   <content type="html">&lt;p&gt;I went to a lot, probably too many conferences in 2012. As the year is over now I'd like to summarize some of the impressions, maybe there's a conference you didn't know about and you'd like to attend this year.&lt;/p&gt; &lt;h4&gt;FOSDEM&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;https://fosdem.org&quot; target=&quot;_blank&quot;&gt;FOSDEM&lt;/a&gt; is the Free and Open Source Software Developer European Meetup, a yearly event that takes place in Brussels, Belgium. There are multiple tracks and developer rooms on a multitude of topics ranging from databases to programming languages and open source tools. The rooms are spread across some buildings at the University so there might be some walking involved when switching tracks. What is rather special is that there's no registration involved, you just go there and that's it. The amount of people can be overwhelming, especially in the main entrance area. Unfortunately I was rather disappointed with the talks I chose. The event is a very good fit if you are working on an Open Source project and, as the name of the conference suggests, want to meet other developers of the project.&lt;/p&gt; &lt;h4&gt;Berlin Expert Days&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.bed-con.de/&quot; target=&quot;_blank&quot;&gt;BED-Con&lt;/a&gt; is a rather young conference organized by the Java User Group Berlin-Brandenburg. I haven't been there in the first year but in 2012 it still had a small and informal feeling. The conference takes place in three rooms of the Freie Universität Berlin, the content selection was an excellent mixture of technical and process/meta talks, most of them in German. If you can afford the trip to Berlin I'd definitively recommend going there.&lt;/p&gt; &lt;h4&gt;JAX&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://jax.de/&quot; target=&quot;_blank&quot;&gt;The largest and best known German Java conference.&lt;/a&gt; There are two editions, JAX in April in Mainz (the one I attended) and W-JAX in November in Munich. There's one huge hall and several smaller rooms and a wide variety of topics you can choose from. I never planned to go there as the admission fee is rather high (thanks to Software &amp;amp; Support for sponsoring my ticket) but I have to admit that it really can be worth the money. There were excellent talks by Charles Nutter, Tim Berglund and many more. The infrastructure (food, coffee, schedules) is very good, if you are on a business budget you can gain a lot by visiting.&lt;/p&gt;   &lt;h4&gt;Berlin Buzzwords&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://berlinbuzzwords.de/&quot; target=&quot;_blank&quot;&gt;A niche conference on Search, Scale and Big Data.&lt;/a&gt; A lot of people are coming from overseas just to visit. If you are interested in these technologies definitively go there. For more information see &lt;a href=&quot;http://blog.florian-hopf.de/2012/06/berlin-buzzwords-2012.html&quot;&gt;this post&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Barcamp Karlsruhe&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.barcamp-karlsruhe.de/&quot; target=&quot;_blank&quot;&gt;My first real Barcamp.&lt;/a&gt; Really fun event but of course there are always some sessions that are not as interesting as anticipated. Topics ranged from Computer and work stuff to more soft content. I always thought this is a nerd only event but as there is so much to choose from Barcamps might even be interesting for people who are not that much into computers. Very well organized, no admission fee, interesting sessions.&lt;/p&gt; &lt;h4&gt;Socrates&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.socrates-conference.de/&quot;&gt;The International Software Craftmanship and Testing Conference.&lt;/a&gt; Awesome setting and the first open space conference I attended. It takes place in a seminar center in the middle of nowhere which makes it a very intense experience. Besides the sessions there are a lot of informal discussions going on around the day with the very enthusiastic attendants. The 2012 event started Thursday evening with a world cafe, Friday and Saturday open space and an optional code retreat on Sunday. I'd say there were three kinds of sessions: informal discussion rounds, practical hands on sessions and talks. It seems that most people liked the practical sessions best, so if I could choose again I'd go to more of those. Thanks to the sponsors all we had to pay was the accommodation and one meal which additionally makes it an incredible cheap event. Be quick with registration as space is limited.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-8aVDubc9AV0/UOVNc4A7F5I/AAAAAAAAAFk/LAbJmWk6t0o/s1600/2012-08-03%2B18.01.43.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; width=&quot;320&quot; src=&quot;http://2.bp.blogspot.com/-8aVDubc9AV0/UOVNc4A7F5I/AAAAAAAAAFk/LAbJmWk6t0o/s320/2012-08-03%2B18.01.43.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;FrOSCon&lt;/h4&gt; &lt;p&gt;The &lt;a href=&quot;http://www.froscon.de/startseite/&quot; target=&quot;_blank&quot;&gt;Free and Open Source Software Conference&lt;/a&gt; is a great community weekend event with different tracks on admin and development topics. I like it a lot because of the variation of talks and the very informal setting. It's a mixture of holiday and learning and for me a chance to get information on topics that are not presented at the other developer conferences I attend. Talks are partly english, partly german. You can either stay in St. Augustin or in Bonn, it's only a short tram ride.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/--dGD_kHmce8/UOVN5f7oYcI/AAAAAAAAAFw/joZu52SyOFM/s1600/2012-08-25%2B16.36.21.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/--dGD_kHmce8/UOVN5f7oYcI/AAAAAAAAAFw/joZu52SyOFM/s320/2012-08-25%2B16.36.21.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;JAX On Tour&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://jax-on-tour.de/&quot; target=&quot;_blank&quot;&gt;JAX On Tour&lt;/a&gt; is another event I attended because of the generous sponsorship of Software &amp;amp; Support. It's not a conference but a training event with longer talks that are grouped together. It's a small event and there's always time for questions. I learned a lot, mainly about documenting architectures. This is a really good alternative to a normal conference to grasp a topic in depth.&lt;/p&gt; &lt;h4&gt;OpenCms-Days&lt;/h4&gt; &lt;p&gt;If you are into OpenCms you probably already heard about &lt;a href=&quot;http://www.opencms-days.org&quot; target=&quot;_blank&quot;&gt;OpenCms-Days&lt;/a&gt;, and if not this is probably not for you. Two days of OpenCms only, used by Alkacon to present new features and by the community to present extensions and projects. I am always impressed that there are people who fly around the world just to attend, but of course this is the only conference of its kind worldwide. There is always something new to learn and it's fun to meet the community.&lt;/p&gt; &lt;h4&gt;DevFest Karlsruhe&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.devfest.info/event/ag9zfmRldmZlc3RnbG9iYWxyDQsSBUV2ZW50GNKsBgw&quot;&gt;A one day event on Google technologies.&lt;/a&gt; There have been several events in different cities worldwide, this one organized by the Google Developer Group Karlsruhe. The organizers were really unlucky as multiple speakers canceled on short notice, nevertheless there were some really good talks. Kudos to the organizers who managed to get this event started in a really short time frame.&lt;/p&gt; &lt;h4&gt;ApacheCon Europe&lt;/h4&gt; &lt;p&gt;I originally went to &lt;a href=&quot;http://www.apachecon.eu/&quot; target=&quot;_blank&quot;&gt;ApacheCon&lt;/a&gt; because it took place in Sinsheim, which is close to Karlsruhe. Fortunately in this year the conference also hosted the LuceneCon Europe, so there were lots of interesting talks for me. Additionally I had been voted as a committer to the &lt;a href=&quot;http://incubator.apache.org/odftoolkit/&quot; target=&quot;_blank&quot;&gt;ODFToolkit&lt;/a&gt; just before it and I was able to meet some other people that are involved in the project. The location was really special (soccer stadium) but I think a lot of non-locals had to suffer a bit because of the lack of hotels and taxis. This community event can be really interesting even if you are only a user of a project.&lt;/p&gt;   &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-216RO9qNaj8/UOVQvGWfO-I/AAAAAAAAAGE/d_VYS67dRS8/s1600/2012-11-06%2B10.10.07.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; width=&quot;320&quot; src=&quot;http://3.bp.blogspot.com/-216RO9qNaj8/UOVQvGWfO-I/AAAAAAAAAGE/d_VYS67dRS8/s320/2012-11-06%2B10.10.07.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;Devoxx&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;http://www.devoxx.com&quot; target=&quot;_blank&quot;&gt;Devoxx&lt;/a&gt; is the largest Java conference in Europe, organized by the Java User Group Belgium. They attract a lot of high class speakers so this is the place to keep you informed on the Java universe. Located in a large multiplex cinema in a suburb of Antwerp, very comfy chairs and huge screens. The week starts with two university days that contain longer talks and are usually less crowded. This year I went there on Wednesday, due to a train strike in Belgium I arrived quite late. I have to admit that it probably wasn't worth the hassle for only 1.5 days. If you are going there I recommend to stay the whole week.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-dVO5wozSqGA/UOVRHYZ-qHI/AAAAAAAAAGQ/M3VAJRLzG2s/s1600/2012-11-14%2B17.51.50.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; width=&quot;320&quot; src=&quot;http://3.bp.blogspot.com/-dVO5wozSqGA/UOVRHYZ-qHI/AAAAAAAAAGQ/M3VAJRLzG2s/s320/2012-11-14%2B17.51.50.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;h4&gt;2013&lt;/h4&gt; &lt;p&gt;So this was a lot last year. I don't plan to visit that many conferences again. I am sure that I will be going to Berlin Buzzwords, Socrates, FrOSCon. There probably will be more, but it won't get 13 this year :).&lt;/p&gt;  &lt;p&gt;Finally, I couldn't have afforded to pay for all those conferences myself, so thanks to &lt;a href=&quot;http://synyx.de/&quot; target=&quot;_blank&quot;&gt;synyx&lt;/a&gt;, who paid for FOSDEM and BEDCon when I was still employed there and provided me with a free ticket to OpenCms-Days. Thanks to &lt;a href=&quot;http://sandsmedia.com/&quot; target=&quot;_blank&quot;&gt;Software &amp;amp; Support&lt;/a&gt; for letting me attend JAX and JAX On Tour for free, those guys are fantastic supporters of the Java User Group Karlsruhe. Also, thanks to the Devoxx team for letting me attend as an ambassador of our JUG.&lt;/p&gt;   </content>
 </entry>
 
 <entry>
   <title>Gradle is too Clever for my Plans</title>
   <link href="http://blog.florian-hopf.de/2012/12/gradle-is-too-clever-for-my-plans.html"/>
   <updated>2012-12-20T14:16:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/12/gradle-is-too-clever-for-my-plans</id>
   <content type="html">&lt;p&gt;While writing &lt;a href=&quot;http://blog.florian-hopf.de/2012/12/looking-at-plaintext-lucene-index.html&quot;&gt;this post about the Lucene Codec API&lt;/a&gt; I noticed something strange when running the tests with &lt;a href=&quot;http://www.gradle.org/&quot; target=&quot;_blank&quot;&gt;Gradle&lt;/a&gt;. When experimenting with a library feature most of the times I write unit tests that validate my expectations. This is a habit I learned from &lt;a href=&quot;http://www.manning.com/hatcher2/&quot; target=&quot;_blank&quot;&gt;Lucene in Action&lt;/a&gt; and can also be useful in real world scenarios, e.g. to make sure that nothing breaks when you update a library.&lt;/p&gt; &lt;p&gt;OK, what happened? This time I did not only want to have the test result but also ran the test for a side effect, I wanted a Lucene index to be written to the /tmp directory to manually have a look at it. This worked fine for the first time, but not afterwards, e.g. after my machine was rebooted and the directory cleared.&lt;/p&gt; &lt;p&gt;It turns out that the Gradle developers know that a test shouldn't be used to execute stuff. So once the test is run successfully it is just not run again &lt;a href=&quot;http://gradle.1045684.n5.nabble.com/how-does-gradle-decide-when-to-run-tests-td3314172.html#none&quot; target=&quot;_blank&quot;&gt;until its input changes&lt;/a&gt;! Though this bit me this time this is a really nice feature to speed up your builds. And if you really need to execute the tests, you can always run &lt;code&gt;gradle cleanTest test&lt;/code&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Looking at a Plaintext Lucene Index</title>
   <link href="http://blog.florian-hopf.de/2012/12/looking-at-plaintext-lucene-index.html"/>
   <updated>2012-12-07T19:45:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/12/looking-at-plaintext-lucene-index</id>
   <content type="html">&lt;p&gt;The &lt;a href=&quot;http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/codecs/lucene40/package-summary.html#package_description&quot; target=&quot;_blank&quot;&gt;Lucene file format&lt;/a&gt; is one of the reasons why Lucene is as fast as it is. An index consist of several binary files that you can't really inspect if you don't use tools like the fantastic &lt;a href=&quot;http://code.google.com/p/luke/&quot; target=&quot;_blank&quot;&gt;Luke&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Starting with Lucene 4 the format for these files can be configured using the &lt;a href=&quot;http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/codecs/package-summary.html#package_description&quot; target=&quot;_blank&quot;&gt;Codec API&lt;/a&gt;. Several implementations are provided with the release, among those the &lt;a href=&quot;http://lucene.apache.org/core/4_0_0/codecs/org/apache/lucene/codecs/simpletext/SimpleTextCodec.html&quot; target=&quot;_blank&quot;&gt;SimpleTextCodec&lt;/a&gt; that can be used to write the files in plaintext for learning and debugging purposes.&lt;/p&gt; &lt;p&gt;To configure the Codec you just set it on the IndexWriterConfig:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);&lt;br /&gt;IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_40, analyzer);&lt;br /&gt;// recreate the index on each execution&lt;br /&gt;config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);&lt;br /&gt;config.setCodec(new SimpleTextCodec());&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The rest of the indexing process stays exactly the same as it used to be:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;Directory luceneDir = FSDirectory.open(plaintextDir);&lt;br /&gt;try (IndexWriter writer = new IndexWriter(luceneDir, config)) {&lt;br /&gt;    writer.addDocument(Arrays.asList(&lt;br /&gt;            new TextField(&quot;title&quot;, &quot;The title of my first document&quot;, Store.YES),&lt;br /&gt;            new TextField(&quot;content&quot;, &quot;The content of the first document&quot;, Store.NO)));&lt;br /&gt;&lt;br /&gt;    writer.addDocument(Arrays.asList(&lt;br /&gt;            new TextField(&quot;title&quot;, &quot;The title of the second document&quot;, Store.YES),&lt;br /&gt;            new TextField(&quot;content&quot;, &quot;And this is the content&quot;, Store.NO)));&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;After running this code the index directory contains several files. Those are not the same type of files that are created using the default codec.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;ls /tmp/lucene-plaintext/&lt;br /&gt;_1_0.len  _1_1.len  _1.fld  _1.inf  _1.pst  _1.si  segments_2  segments.gen&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The segments_x file is the starting point (x depends on the amount of times you have written to the index before and starts with 1). This still is a binary file but contains the information which codec is used to write to the index. It contains the name of each Codec that is used for writing a certain segment.&lt;/p&gt; &lt;p&gt;The rest of the index files are all plaintext. They do not contain the same information as their binary cousins. For example the .pst file represents the complete posting list, the structure you normally mean when talking about an inverted index:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;field content&lt;br /&gt;  term content&lt;br /&gt;    doc 0&lt;br /&gt;      freq 1&lt;br /&gt;      pos 1&lt;br /&gt;    doc 1&lt;br /&gt;      freq 1&lt;br /&gt;      pos 4&lt;br /&gt;  term document&lt;br /&gt;    doc 0&lt;br /&gt;      freq 1&lt;br /&gt;      pos 5&lt;br /&gt;  term first&lt;br /&gt;    doc 0&lt;br /&gt;      freq 1&lt;br /&gt;      pos 4&lt;br /&gt;field title&lt;br /&gt;  term document&lt;br /&gt;    doc 0&lt;br /&gt;      freq 1&lt;br /&gt;      pos 5&lt;br /&gt;    doc 1&lt;br /&gt;      freq 1&lt;br /&gt;      pos 5&lt;br /&gt;  term first&lt;br /&gt;    doc 0&lt;br /&gt;      freq 1&lt;br /&gt;      pos 4&lt;br /&gt;  term my&lt;br /&gt;    doc 0&lt;br /&gt;      freq 1&lt;br /&gt;      pos 3&lt;br /&gt;  term second&lt;br /&gt;    doc 1&lt;br /&gt;      freq 1&lt;br /&gt;      pos 4&lt;br /&gt;  term title&lt;br /&gt;    doc 0&lt;br /&gt;      freq 1&lt;br /&gt;      pos 1&lt;br /&gt;    doc 1&lt;br /&gt;      freq 1&lt;br /&gt;      pos 1&lt;br /&gt;END&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The content that is marked as stored resides in the .fld file:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;doc 0&lt;br /&gt;  numfields 1&lt;br /&gt;  field 0&lt;br /&gt;    name title&lt;br /&gt;    type string&lt;br /&gt;    value The title of my first document&lt;br /&gt;doc 1&lt;br /&gt;  numfields 1&lt;br /&gt;  field 0&lt;br /&gt;    name title&lt;br /&gt;    type string&lt;br /&gt;    value The title of the second document&lt;br /&gt;END&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;If you'd like to have a look at the rest of the files checkout the code at &lt;a href=&quot;https://github.com/fhopf/lucene-codec-example&quot; target=&quot;_blank&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;The SimpleTextCodec only is an interesting byproduct. The Codec API can be used for a lot useful things. For example the feature to read indices of older Lucene versions is implemented using seperate codecs. Also, you can mix several Codecs in an index so reindexing on version updates should not be necessary immediately. I am sure more useful codecs will pop up in the future.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Getting rid of synchronized: Using Akka from Java</title>
   <link href="http://blog.florian-hopf.de/2012/08/getting-rid-of-synchronized-using-akka.html"/>
   <updated>2012-08-23T14:02:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/08/getting-rid-of-synchronized-using-akka</id>
   <content type="html">&lt;p&gt;&lt;i&gt;I've been giving an &lt;a href=&quot;http://slideshare.net/fhopf/akka-presentation-schulesynyx&quot; target=&quot;_blank&quot;&gt;internal talk&lt;/a&gt; on &lt;a href=&quot;http://akka.io&quot; target=&quot;_blank&quot;&gt;Akka&lt;/a&gt;, the Actor framework for the JVM, at my former company &lt;a href=&quot;http://synyx.de&quot; target=&quot;_blank&quot;&gt;synyx&lt;/a&gt;. For the talk I implemented a small example application, kind of a web crawler, using Akka. I published the &lt;a href=&quot;https://github.com/fhopf/akka-crawler-example&quot; target=&quot;_blank&quot;&gt;source code on Github&lt;/a&gt; and will explain some of the concepts in this post.&lt;/i&gt;&lt;/p&gt; &lt;h4&gt;Motivation&lt;/h4&gt; &lt;p&gt;To see why you might need something like Akka, think you want to implement a simple web crawler for offline search. You are downloading pages from a certain location, parse and index the content and follow any links that you haven't parsed and indexed yet. I am using &lt;a href=&quot;http://htmlparser.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;HtmlParser&lt;/a&gt; for downloading and parsing pages and &lt;a href=&quot;http://lucene.apache.org&quot; target=&quot;_blank&quot;&gt;Lucene&lt;/a&gt; for indexing them. The logic is contained in two service objects, PageRetriever and Indexer, that can be used from our main application.&lt;/p&gt; &lt;p&gt;A simple sequential execution might then look something like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public void downloadAndIndex(String path, IndexWriter writer) {&lt;br /&gt;    VisitedPageStore pageStore = new VisitedPageStore();&lt;br /&gt;    pageStore.add(path);&lt;br /&gt;        &lt;br /&gt;    Indexer indexer = new IndexerImpl(writer);&lt;br /&gt;    PageRetriever retriever = new HtmlParserPageRetriever(path);&lt;br /&gt;        &lt;br /&gt;    String page;&lt;br /&gt;    while ((page = pageStore.getNext()) != null) {&lt;br /&gt;        PageContent pageContent = retriever.fetchPageContent(page);&lt;br /&gt;        pageStore.addAll(pageContent.getLinksToFollow());&lt;br /&gt;        indexer.index(pageContent);&lt;br /&gt;        pageStore.finished(page);&lt;br /&gt;    }&lt;br /&gt;        &lt;br /&gt;    indexer.commit();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We are starting with one page, extract the content and the links, index the content and store all links that are to be visited in the VisitedPageStore. This class contains the logic to determine which links are visited already. We are looping as long as there are more links to follow, once we are done we commit the Lucene IndexWriter.&lt;/p&gt; &lt;p&gt;This implementation works fine, when running on my outdated laptop it will finish in around 3 seconds for an example page. (Note that the times I am giving are by no means meant as a benchmark but are just there to give you some idea on the numbers).&lt;/p&gt; &lt;p&gt;So we are done? No, of course we can do better by optimizing the resources we have available. Let's try to improve this solution by splitting it into several tasks that can be executed in parallel.&lt;/p&gt;   &lt;h4&gt;Shared State Concurrency&lt;/h4&gt; &lt;p&gt;The normal way in Java would be to implement several Threads that do parts of the work and access the state via guarded blocks, e.g. by synchronizing methods. So in our case there might be several Threads that access our global state that is stored in the VisitedPageStore.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-c2Pir19FrKY/UC5gu58ACPI/AAAAAAAAAEk/GjHFDb7q2Fc/s1600/synchronize.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;101&quot; width=&quot;320&quot; src=&quot;http://4.bp.blogspot.com/-c2Pir19FrKY/UC5gu58ACPI/AAAAAAAAAEk/GjHFDb7q2Fc/s320/synchronize.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;This model is what Venkat Subramaniam calls Synchronize and Suffer in his great book &lt;a href=&quot;http://pragprog.com/book/vspcon/programming-concurrency-on-the-jvm&quot; target=&quot;_blank&quot;&gt;Programming Concurrency on the JVM&lt;/a&gt;. Working with Threads and building correct solutions might not seem that hard at first but is inherintly difficult. I like those two tweets that illustrate the problem:&lt;/p&gt; &lt;blockquote class=&quot;twitter-tweet tw-align-center&quot; lang=&quot;de&quot;&gt;&lt;p&gt;Adding the &quot;synchronized&quot; keyword to Java was a mistake. Makes people believe they can write multi-threaded code.&lt;/p&gt;&amp;mdash; Erik Dörnenburg (@erikdoe) &lt;a href=&quot;https://twitter.com/erikdoe/status/128817862268821504&quot; data-datetime=&quot;2011-10-25T12:59:06+00:00&quot;&gt;Oktober 25, 2011&lt;/a&gt;&lt;/blockquote&gt; &lt;blockquote class=&quot;twitter-tweet tw-align-center&quot; lang=&quot;de&quot;&gt;&lt;p&gt;95% of syncronized code is broken. The other 5% is written by Brian Goetz. - Venkat Subramaniam at &lt;a href=&quot;https://twitter.com/search/?q=%23s2gx&quot;&gt;&lt;s&gt;#&lt;/s&gt;&lt;b&gt;s2gx&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;&amp;mdash; Ronny Løvtangen (@rlovtangen) &lt;a href=&quot;https://twitter.com/rlovtangen/status/129323480159232000&quot; data-datetime=&quot;2011-10-26T22:28:15+00:00&quot;&gt;Oktober 26, 2011&lt;/a&gt;&lt;/blockquote&gt;&lt;script src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt; &lt;p&gt;Brian Goetz of course being the author of the de-facto standard book on the new Java concurrency features, &lt;a href=&quot;http://jcip.net/&quot; target=&quot;_blank&quot;&gt;Java Concurrency in Practice&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Akka&lt;/h4&gt; &lt;p&gt;So what is Akka? It's an Actor framework for the JVM that is implemented in Scala but that is something that you rarely notice when working from Java. It offers a nice Java API that provides most of the functionality in a convenient way.&lt;/p&gt; &lt;p&gt;Actors are a concept that was introduced in the seventies but became widely known as one of the core features of Erlang, a language to build fault tolerant, self healing systems. Actors employ the concept of Message Passing Concurrency. That means that Actors only communicate by means of messages that are passed into an Actors mailbox. Actors can contain state that they shield from the rest of the system. The only way to change the state is by passing in messages. Each Actor is executed in a different Thread but they provide a higher level of abstraction than working with Threads directly.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-hQwp7o8cH_s/UC5hEJ9IkAI/AAAAAAAAAEw/CVqfjT2Z0nY/s1600/actors.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;151&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-hQwp7o8cH_s/UC5hEJ9IkAI/AAAAAAAAAEw/CVqfjT2Z0nY/s320/actors.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;When implementing Actors you put the behaviour in a method receive() that can act on incoming messages. You can then reply asynchronously to the sender or send messages to any other Actor.&lt;/p&gt; &lt;p&gt;For our problem at hand an Actor setup might look something like this:&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-I5TC1mL-bHI/UC5hMo0rtkI/AAAAAAAAAE8/6OGAL2hsBzg/s1600/actor-setup.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;214&quot; width=&quot;320&quot; src=&quot;http://2.bp.blogspot.com/-I5TC1mL-bHI/UC5hMo0rtkI/AAAAAAAAAE8/6OGAL2hsBzg/s320/actor-setup.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;There is one Master Actor that also contains the global state. It sends a message to fetch a certain page to a PageParsingActor that asynchonously responds to the Master with the PageContent. The Master can then send the PageContent to an IndexingActor which responds with another message. With this setup we have done a first step to scale our solution. There are now three Actors that can be run on different cores of your machine.&lt;/p&gt; &lt;p&gt;Actors are instantiated from other Actors. On the top there's the ActorSystem that is provided by the framework. The MasterActor is instaciated from the ActorSystem:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;ActorSystem actorSystem = ActorSystem.create();&lt;br /&gt;final CountDownLatch countDownLatch = new CountDownLatch(1);&lt;br /&gt;ActorRef master = actorSystem.actorOf(new Props(new UntypedActorFactory() {&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public Actor create() {&lt;br /&gt;        return new SimpleActorMaster(new HtmlParserPageRetriever(path), writer, countDownLatch);&lt;br /&gt;    }&lt;br /&gt;}));&lt;br /&gt;&lt;br /&gt;master.tell(path);&lt;br /&gt;try {&lt;br /&gt;    countDownLatch.await();&lt;br /&gt;    actorSystem.shutdown();&lt;br /&gt;} catch (InterruptedException ex) {&lt;br /&gt;    throw new IllegalStateException(ex);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Ignore the CountdownLatch as it is only included to make it possible to terminate the application. Note that we are not referencing an instance of our class but an ActorRef, a reference to an actor. You will see later why this is important.&lt;/p&gt; &lt;p&gt;The MasterActor contains references to the other Actors and creates them from its context. This makes the two Actors children of the Master:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public SimpleActorMaster(final PageRetriever pageRetriever, final IndexWriter indexWriter,&lt;br /&gt;    final CountDownLatch latch) {&lt;br /&gt;&lt;br /&gt;    super(latch);&lt;br /&gt;    this.indexer = getContext().actorOf(new Props(new UntypedActorFactory() {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        public Actor create() {&lt;br /&gt;&lt;br /&gt;            return new IndexingActor(new IndexerImpl(indexWriter));&lt;br /&gt;        }&lt;br /&gt;    }));&lt;br /&gt;&lt;br /&gt;    this.parser = getContext().actorOf(new Props(new UntypedActorFactory() {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        public Actor create() {&lt;br /&gt;&lt;br /&gt;           return new PageParsingActor(pageRetriever);&lt;br /&gt;        }&lt;br /&gt;    }));&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The PageParsingActor acts on messages to fetch pages and sends a message with the result to the sender:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public void onReceive(Object o) throws Exception {&lt;br /&gt;    if (o instanceof String) {&lt;br /&gt;        PageContent content = pageRetriever.fetchPageContent((String) o);&lt;br /&gt;        getSender().tell(content, getSelf());&lt;br /&gt;    } else {&lt;br /&gt;        // fail on any message we don't expect&lt;br /&gt;        unhandled(o);&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The IndexingActor contains some state with the Indexer. It acts on messages to index pages and to commit the indexing process.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public void onReceive(Object o) throws Exception {&lt;br /&gt;    if (o instanceof PageContent) {&lt;br /&gt;        PageContent content = (PageContent) o;&lt;br /&gt;        indexer.index(content);&lt;br /&gt;        getSender().tell(new IndexedMessage(content.getPath()), getSelf());&lt;br /&gt;    } else if (COMMIT_MESSAGE == o) {&lt;br /&gt;        indexer.commit();&lt;br /&gt;        getSender().tell(COMMITTED_MESSAGE, getSelf());&lt;br /&gt;    } else {&lt;br /&gt;        unhandled(o);&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The MasterActor finally orchestrates the other Actors in its receive() method. It starts with one page and sends it to the PageParsingActor. It keeps the valuable state of the application in the VisitedPageStore. When no more pages are to be fetched and indexed it sends a commit message and terminates the application.&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public void onReceive(Object message) throws Exception {&lt;br /&gt;&lt;br /&gt;    if (message instanceof String) {&lt;br /&gt;        // start&lt;br /&gt;        String start = (String) message;&lt;br /&gt;        visitedPageStore.add(start);&lt;br /&gt;        getParser().tell(visitedPageStore.getNext(), getSelf());&lt;br /&gt;    } else if (message instanceof PageContent) {&lt;br /&gt;        PageContent content = (PageContent) message;&lt;br /&gt;        getIndexer().tell(content, getSelf());&lt;br /&gt;        visitedPageStore.addAll(content.getLinksToFollow());&lt;br /&gt;&lt;br /&gt;        if (visitedPageStore.isFinished()) {&lt;br /&gt;            getIndexer().tell(IndexingActor.COMMIT_MESSAGE, getSelf());&lt;br /&gt;        } else {&lt;br /&gt;            for (String page : visitedPageStore.getNextBatch()) {&lt;br /&gt;                getParser().tell(page, getSelf());&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    } else if (message instanceof IndexedMessage) {&lt;br /&gt;        IndexedMessage indexedMessage = (IndexedMessage) message;&lt;br /&gt;        visitedPageStore.finished(indexedMessage.path);&lt;br /&gt;&lt;br /&gt;        if (visitedPageStore.isFinished()) {&lt;br /&gt;            getIndexer().tell(IndexingActor.COMMIT_MESSAGE, getSelf());&lt;br /&gt;        }&lt;br /&gt;    } else if (message == IndexingActor.COMMITTED_MESSAGE) {&lt;br /&gt;        logger.info(&quot;Shutting down, finished&quot;);&lt;br /&gt;        getContext().system().shutdown();&lt;br /&gt;        countDownLatch.countDown();&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;What happens if we run this example? Unfortunately it now takes around 3.5 seconds on my dual core machine. Though we are now able to run on both cores we have actually decreased the speed of the application. This is probably an important lesson. When building scalable applications it might happen that you are introducing some overhead that decreases the performance when running in the small. Scalability is not about increasing performance but about the ability to distribute the load. &lt;/p&gt; &lt;p&gt;So it was an failure to switch to Akka? Not at all. It turns out that most of the time the application is fetching and parsing pages. This includes waiting for the network. Indexing in Lucene is blazing fast and the Master mostly only dispatches messages. So what can we do about it? We already have split our application into smaller chunks. Fortunately the PageParsingActor doesn't contain any state at all. That means we can easily parallelize its tasks.&lt;/p&gt; &lt;p&gt;This is where the talking to references is important. For an Actor it's transparent if there is one or a million Actors behind a reference. There is one mailbox for an Actor reference that can dispatch the messages to any amount of Actors.&lt;/p&gt; &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-klg16pejZZs/UC5hjo8UpCI/AAAAAAAAAFI/DIeTzalkdEU/s1600/routing.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;174&quot; width=&quot;320&quot; src=&quot;http://3.bp.blogspot.com/-klg16pejZZs/UC5hjo8UpCI/AAAAAAAAAFI/DIeTzalkdEU/s320/routing.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;We only need to change the instanciation of the Actor, the rest of the application remains the same:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;parser = getContext().actorOf(new Props(new UntypedActorFactory() {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        public Actor create() {&lt;br /&gt;&lt;br /&gt;            return new PageParsingActor(pageRetriever);&lt;br /&gt;        }&lt;br /&gt;}).withRouter(new RoundRobinRouter(10)));&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;By using a router the Akka framework automatically takes care that there are 10 Actors available. The messages are distributed to any available Actor. This takes the runtime down to 2 seconds.&lt;/p&gt; &lt;h4&gt;A word on Blocking&lt;/h4&gt; &lt;p&gt;Note that the way I am doing network requests here is not recommended in Akka. HTMLParser is doing blocking networking which should be carefully reconsidered when designing a reactive system. In fact, as this application is highly network bound, we might even gain more benefit by just using an asynchronous networking library. But hey, then I wouldn't be able to tell you how nice it is to use Akka. In a future post I will highlight some more Akka features that can help to make our application more robust and fault tolerant.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Slides and demo code for my talk at JUG KA available</title>
   <link href="http://blog.florian-hopf.de/2012/07/slides-and-demo-code-for-my-talk-at-jug.html"/>
   <updated>2012-07-06T00:50:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/07/slides-and-demo-code-for-my-talk-at-jug</id>
   <content type="html">&lt;p&gt;I just uploaded the &lt;a href=&quot;http://www.florian-hopf.de/files/lucene-solr-jugka-040712.pdf&quot;&gt;(german) slides&lt;/a&gt; as well as the &lt;a href=&quot;https://github.com/fhopf/lucene-solr-talk&quot;&gt;example code&lt;/a&gt; for yesterdays talk on &lt;a href=&quot;http://lucene.apache.org&quot;&gt;Lucene&lt;/a&gt; and &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Solr&lt;/a&gt; at our local &lt;a href=&quot;http://jug-ka.de&quot;&gt;Java User Group&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;The demo application contains several subprojects for indexing and searching with Lucene and Solr as well as a simple Dropwizard application that demonstrates some search features. See the README files in the source tree to find out how to run the application.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Dropwizard Encoding Woes</title>
   <link href="http://blog.florian-hopf.de/2012/06/dropwizard-encoding-woes.html"/>
   <updated>2012-06-29T15:57:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/06/dropwizard-encoding-woes</id>
   <content type="html">&lt;p&gt;I have been working on an example application for Lucene and Solr for my upcoming talk at the &lt;a href=&quot;http://jug-ka.de&quot;&gt;Java User Group Karlsruhe&lt;/a&gt;. As a web framework I wanted to try &lt;a href=&quot;http://dropwizard.codahale.com&quot;&gt;Dropwizard&lt;/a&gt;, a lightweight application framework that can expose resources via JAX-RS, provides out of the box monitoring support and can render resource representations using Freemarker. It's really easy to get started, there's a good &lt;a href=&quot;http://dropwizard.codahale.com/getting-started/&quot;&gt;tutorial&lt;/a&gt; and the &lt;a href=&quot;http://dropwizard.codahale.com/manual/&quot;&gt;manual&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;An example resource might look like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;import javax.ws.rs.GET;&lt;br /&gt;import javax.ws.rs.Path;&lt;br /&gt;import javax.ws.rs.Produces;&lt;br /&gt;import javax.ws.rs.core.MediaType;&lt;br /&gt;&lt;br /&gt;@Path(&quot;/example&quot;)&lt;br /&gt;@Produces(MediaType.TEXT_HTML)&lt;br /&gt;public class ExampleResource {&lt;br /&gt;&lt;br /&gt;    @GET&lt;br /&gt;    public ExampleView illustrate() {&lt;br /&gt;        return new ExampleView(&quot;Mot\u00f6rhead&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The Resource produces HTML using Freemarker, which is possible if you add the view bundle in the service. There is one method that is called when the resource is addressed using GET. Inside the method we create a view object accepting a message that in this case contains the umlaut 'ö'. The view class that is returned by the method looks like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;import com.yammer.dropwizard.views.View;&lt;br /&gt;&lt;br /&gt;public class ExampleView extends View {&lt;br /&gt;&lt;br /&gt;    private final String message;&lt;br /&gt;&lt;br /&gt;    public ExampleView(String message) {&lt;br /&gt;        super(&quot;example.fmt&quot;);&lt;br /&gt;        this.message = message;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String getMessage() {&lt;br /&gt;        return message;&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;It accepts a message as constructor parameter. The template name is passed to the parent class. This view class is now available in a freemarker template, an easy variant looks like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;br /&gt;    &amp;lt;body&amp;gt;&lt;br /&gt;        &amp;lt;h1&amp;gt;${message} rocks!&amp;lt;/h1&amp;gt;&lt;br /&gt;    &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; If I run this on my machine and access it with Firefox it doesn't work as expected. The umlaut character is broken, something Lemmy surely doesn't approve:  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-I88R0n2P9EM/T-1fKj1_a4I/AAAAAAAAADo/S8VWbvJiY4g/s1600/broken.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;99&quot; width=&quot;320&quot; src=&quot;http://1.bp.blogspot.com/-I88R0n2P9EM/T-1fKj1_a4I/AAAAAAAAADo/S8VWbvJiY4g/s320/broken.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Accessing the resource using curl works flawlessly:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;curl http://localhost:8080/example&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;    &amp;lt;body&amp;gt;&lt;br /&gt;        &amp;lt;h1&amp;gt;Mot&amp;#246;rhead rocks!&amp;lt;/h1&amp;gt;&lt;br /&gt;    &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Why is that? It's Servlet Programming 101: You need to set the character encoding of the response. My Firefox defaults to ISO-8859-1, curl seems to use UTF-8 by default. How can we fix it? Tell the client which encoding we are using, which can be done using the Produces annotation:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;@Produces(&quot;text/html; charset=utf-8&quot;)&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;So what does it have to do with Dropwizard? Nothing really, it's a JAX-RS thing. All components in Dropwizard (Jetty and Freemarker notably) are using UTF-8 by default.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Running and Testing Solr with Gradle</title>
   <link href="http://blog.florian-hopf.de/2012/06/running-and-testing-solr-with-gradle.html"/>
   <updated>2012-06-20T20:28:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/06/running-and-testing-solr-with-gradle</id>
   <content type="html">&lt;p&gt;A while ago I blogged on &lt;a href=&quot;http://blog.synyx.de/2011/01/integration-tests-for-your-solr-config/&quot;&gt;testing Solr with Maven&lt;/a&gt; on the &lt;a href=&quot;http://blog.synyx.de&quot;&gt;synyx blog&lt;/a&gt;. In this post I will show you how to setup a similar project with &lt;a href=&quot;http://gradle.org/&quot;&gt;Gradle&lt;/a&gt; that can start the &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Solr&lt;/a&gt; webapp and execute tests against your configuration.&lt;/p&gt;&lt;h4&gt;Running Solr&lt;/h4&gt;&lt;p&gt;Solr is running as a webapp in any JEE servlet container like Tomcat or Jetty. The index and search configuration resides in a directory commonly referred to as Solr home that can be outside of the webapp directory. This is also the place where the Lucene index files are created. The location for Solr home can be set using an environment variable.&lt;/p&gt;&lt;p&gt;The Solr war file is available in Maven Central. &lt;a href=&quot;http://tadtech.blogspot.de/2012/03/run-jetty-on-dependency-resolved-war-in.html&quot;&gt;This post&lt;/a&gt; describes how to run a war file that is deployed in a Maven repository using Gradle. Let's see how the Gradle build file looks like for running Solr:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;import org.gradle.api.plugins.jetty.JettyRunWar&lt;br /&gt;&lt;br /&gt;apply plugin: 'java'&lt;br /&gt;apply plugin: 'jetty'&lt;br /&gt;&lt;br /&gt;repositories {&lt;br /&gt;    mavenCentral()&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// custom configuration for running the webapp&lt;br /&gt;configurations {&lt;br /&gt;    solrWebApp&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;dependencies {&lt;br /&gt;    solrWebApp &quot;org.apache.solr:solr:3.6.0@war&quot;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// custom task that configures the jetty plugin&lt;br /&gt;task runSolr(type: JettyRunWar) {&lt;br /&gt;    webApp = configurations.solrWebApp.singleFile&lt;br /&gt;&lt;br /&gt;    // jetty configuration&lt;br /&gt;    httpPort = 8082&lt;br /&gt;    contextPath = 'solr'&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// executed before jetty starts&lt;br /&gt;runSolr.doFirst {&lt;br /&gt;    System.setProperty(&quot;solr.solr.home&quot;, &quot;./solrhome&quot;)&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We are creating a custom configuration that contains the Solr war file. In the task runSolr we configure the Jetty plugin. To add the Solr home environment variable we can use the way &lt;a href=&quot;http://www.sebastian.himberger.de/blog/2011/07/16/adding-custom-jvm-properties-to-gradles-jettyrun/&quot;&gt;described by Sebastian Himberger&lt;/a&gt;. We add a code block that is executed before Jetty starts and sets the environment variable using standard Java mechanisms. You can now start Solr using &lt;em&gt;gradle runSolr&lt;/em&gt;. You will see some errors regarding multiple versions of slf4j that are very like caused by &lt;a href=&quot;http://issues.gradle.org/browse/GRADLE-897&quot;&gt;this bug&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Testing the Solr configuration&lt;/h4&gt;&lt;p&gt;Solr provides some classes that start an embedded instance using your configuration. You can use these classes in any setup as they do not depend on the gradle jetty plugin. Starting with Solr 3.2 the test framework is not included in solr-core anymore. This is what the relevant part of the dependency section looks like now:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;testCompile &quot;junit:junit:4.10&quot;&lt;br /&gt;testCompile &quot;org.apache.solr:solr-test-framework:3.6.0&quot;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you can place a test in &lt;em&gt;src/test/java&lt;/em&gt; that either uses the convenience methods provided by SolrTestCaseJ4 or you can instantiate an EmbeddedSolrServer and execute any SolrJ actions. Both of these ways will use your custom config. This way you can easily validate that configuration changes don't break existing functionality. An example of using the convenience methods:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;import org.apache.solr.SolrTestCaseJ4;&lt;br /&gt;import org.apache.solr.client.solrj.SolrServerException;&lt;br /&gt;import org.junit.BeforeClass;&lt;br /&gt;import org.junit.Test;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;&lt;br /&gt;public class BasicConfigurationTest extends SolrTestCaseJ4 {&lt;br /&gt;&lt;br /&gt;    @BeforeClass&lt;br /&gt;    public static void initCore() throws Exception {&lt;br /&gt;        SolrTestCaseJ4.initCore(&quot;solrhome/conf/solrconfig.xml&quot;, &quot;solrhome/conf/schema.xml&quot;, &quot;solrhome/&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void noResultInEmptyIndex() throws SolrServerException {&lt;br /&gt;        assertQ(&quot;test query on empty index&quot;,&lt;br /&gt;                req(&quot;text that is not found&quot;)&lt;br /&gt;                , &quot;//result[@numFound='0']&quot;&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void pathIsMandatory() throws SolrServerException, IOException {&lt;br /&gt;        assertFailedU(adoc(&quot;title&quot;, &quot;the title&quot;));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void simpleDocumentIsIndexedAndFound() throws SolrServerException, IOException {&lt;br /&gt;        assertU(adoc(&quot;path&quot;, &quot;/tmp/foo&quot;, &quot;content&quot;, &quot;Some important content.&quot;));&lt;br /&gt;        assertU(commit());&lt;br /&gt;&lt;br /&gt;        assertQ(&quot;added document found&quot;,&lt;br /&gt;                req(&quot;important&quot;)&lt;br /&gt;                , &quot;//result[@numFound='1']&quot;&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We extend the class &lt;a href=&quot;http://lucene.apache.org/solr//api/test-framework/org/apache/solr/SolrTestCaseJ4.html&quot;&gt;SolrTestCaseJ4&lt;/a&gt; that is responsible for creating the core and instanciating the runtime using the paths we provide with the method &lt;em&gt;initCore()&lt;/em&gt;. Using the available assert methods you can execute queries and validate the result using XPath expressions.&lt;/p&gt; &lt;p&gt;An example that instanciates a SolrServer might look like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;import org.apache.solr.SolrTestCaseJ4;&lt;br /&gt;import org.apache.solr.client.solrj.SolrQuery;&lt;br /&gt;import org.apache.solr.client.solrj.SolrServerException;&lt;br /&gt;import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;&lt;br /&gt;import org.apache.solr.client.solrj.response.FacetField;&lt;br /&gt;import org.apache.solr.client.solrj.response.QueryResponse;&lt;br /&gt;import org.apache.solr.common.SolrInputDocument;&lt;br /&gt;import org.apache.solr.common.params.SolrParams;&lt;br /&gt;import org.junit.After;&lt;br /&gt;import org.junit.Before;&lt;br /&gt;import org.junit.BeforeClass;&lt;br /&gt;import org.junit.Test;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;&lt;br /&gt;public class ServerBasedTalkTest extends SolrTestCaseJ4 {&lt;br /&gt;&lt;br /&gt;    private EmbeddedSolrServer server;&lt;br /&gt;&lt;br /&gt;    @BeforeClass&lt;br /&gt;    public static void initCore() throws Exception {&lt;br /&gt;        SolrTestCaseJ4.initCore(&quot;solr/conf/solrconfig.xml&quot;, &quot;solr/conf/schema.xml&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void initServer() {&lt;br /&gt;        server = new EmbeddedSolrServer(h.getCoreContainer(), h.getCore().getName());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void queryOnEmptyIndexNoResults() throws SolrServerException {&lt;br /&gt;        QueryResponse response = server.query(new SolrQuery(&quot;text that is not found&quot;));&lt;br /&gt;        assertTrue(response.getResults().isEmpty());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void singleDocumentIsFound() throws IOException, SolrServerException {&lt;br /&gt;        SolrInputDocument document = new SolrInputDocument();&lt;br /&gt;        document.addField(&quot;path&quot;, &quot;/tmp/foo&quot;);&lt;br /&gt;        document.addField(&quot;content&quot;, &quot;Mein Hut der hat 4 Ecken&quot;);&lt;br /&gt;&lt;br /&gt;        server.add(document);&lt;br /&gt;        server.commit();&lt;br /&gt;&lt;br /&gt;        SolrParams params = new SolrQuery(&quot;ecke&quot;);&lt;br /&gt;        QueryResponse response = server.query(params);&lt;br /&gt;        assertEquals(1L, response.getResults().getNumFound());&lt;br /&gt;        assertEquals(&quot;/tmp/foo&quot;, response.getResults().get(0).get(&quot;path&quot;));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @After&lt;br /&gt;    public void clearIndex() {&lt;br /&gt;        super.clearIndex();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The tests can now be executed using &lt;em&gt;gradle test&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;Testing your Solr configuration is important as changes in one place might easily lead to side effects with another search functionality. I recommend to add tests even for basic functionality and evolve the tests with your project.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Reading term values for fields from a Lucene Index</title>
   <link href="http://blog.florian-hopf.de/2012/06/reading-term-values-for-fields-from.html"/>
   <updated>2012-06-16T15:13:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/06/reading-term-values-for-fields-from</id>
   <content type="html">&lt;p&gt;Sometimes when using Lucene you might want to retrieve all term values for a given field. Think of categories that you want to display as search links or in a filtering dropdown box. Indexing might look something like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36));&lt;br /&gt;IndexWriter writer = new IndexWriter(directory, config);&lt;br /&gt;&lt;br /&gt;Document doc = new Document();&lt;br /&gt;&lt;br /&gt;doc.add(new Field(&quot;Category&quot;, &quot;Category1&quot;, Field.Store.NO, Field.Index.NOT_ANALYZED));&lt;br /&gt;doc.add(new Field(&quot;Category&quot;, &quot;Category2&quot;, Field.Store.NO, Field.Index.NOT_ANALYZED));&lt;br /&gt;doc.add(new Field(&quot;Author&quot;, &quot;Florian Hopf&quot;, Field.Store.NO, Field.Index.NOT_ANALYZED));&lt;br /&gt;writer.addDocument(doc);&lt;br /&gt;&lt;br /&gt;doc.add(new Field(&quot;Category&quot;, &quot;Category3&quot;, Field.Store.NO, Field.Index.NOT_ANALYZED));&lt;br /&gt;doc.add(new Field(&quot;Category&quot;, &quot;Category2&quot;, Field.Store.NO, Field.Index.NOT_ANALYZED));&lt;br /&gt;doc.add(new Field(&quot;Author&quot;, &quot;Theo Tester&quot;, Field.Store.NO, Field.Index.NOT_ANALYZED));&lt;br /&gt;writer.addDocument(doc);&lt;br /&gt;&lt;br /&gt;writer.close();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;We are adding two documents, one that is assigned Category1 and Category2 and one that is assigned Category2 and Category3. Note that we are adding both fields unanalyzed so the Strings are added to the index as they are. Lucenes index looks something like this afterwards:&lt;/p&gt; &lt;table class=&quot;table&quot;&gt;&lt;tr&gt;&lt;th&gt;Field&lt;/th&gt;&lt;th&gt;Term&lt;/th&gt;&lt;th&gt;Documents&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Author&lt;/td&gt;&lt;td&gt;Florian Hopf&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Theo Tester&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Category&lt;/td&gt;&lt;td&gt;Category1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Category2&lt;/td&gt;&lt;td&gt;1, 2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Category3&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;The fields are sorted alphabetically by fieldname first and then by term value. You can access the values using the IndexReaders &lt;em&gt;terms()&lt;/em&gt; method that returns a TermEnum. You can instruct the IndexReader to start with a certain term so you can directly jump to the category without having to iterate all values. But before we do this let's look at how we are used to access Enumeration values in Java:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;Enumeration en = ...;&lt;br /&gt;while(en.hasMoreElements()) {&lt;br /&gt;    Object obj = en.nextElement();&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;In a while-loop we are checking if there is another element and retrieve it inside the loop. As this pattern is very common when iterating the terms with Lucene you might end with something like this (Note that all the examples here are missing the stop condition. If there are more fields the terms of those fields will also be iterated):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;TermEnum terms = reader.terms(new Term(&quot;Category&quot;));&lt;br /&gt;// this code is broken, don't use&lt;br /&gt;while(terms.next()) {&lt;br /&gt;    Term term = terms.term();&lt;br /&gt;    System.out.println(term.text());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The &lt;em&gt;next()&lt;/em&gt; method returns a boolean if there are more elements and points to the next element. The &lt;em&gt;term()&lt;/em&gt; method then can be used to retrieve the Term. But this doesn't work as expected. The code only finds Category2 and Category3 but skips Category1. Why is that? The Lucene TermEnum works differently than we are used from Java Enumerations. When the TermEnum is returned it already points to the first element so with &lt;em&gt;next()&lt;/em&gt; we skip this first element.&lt;/p&gt; &lt;p&gt;This snippet instead works correctly using a for loop:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;TermEnum terms = reader.terms(new Term(&quot;Category&quot;));&lt;br /&gt;for(Term term = terms.term(); term != null; terms.next(), term = terms.term()) {&lt;br /&gt;    System.out.println(term.text());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Or you can use a do while loop with a check for the first element:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;TermEnum terms = reader.terms(new Term(&quot;Category&quot;));&lt;br /&gt;if (terms.term() != null) {&lt;br /&gt;    do {&lt;br /&gt;        Term term = terms.term();&lt;br /&gt;        System.out.println(term.text());&lt;br /&gt;    } while(terms.next());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;You can't really blame Lucene for this as the methods are aptly named. It's our habits that lead to minor errors like this. &lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Berlin Buzzwords 2012</title>
   <link href="http://blog.florian-hopf.de/2012/06/berlin-buzzwords-2012.html"/>
   <updated>2012-06-07T00:20:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/06/berlin-buzzwords-2012</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://berlinbuzzwords.de/&quot;&gt;Berlin Buzzwords&lt;/a&gt; is an annual conference on search, store and scale technology. I've heard good things about it before and finally got convinced to go there this year. The conference itself lasts for two days but there are additional events before and afterwards so if you like you can spend a whole week.&lt;/p&gt; &lt;h4&gt;The Barcamp&lt;/h4&gt; &lt;p&gt;As I had to travel on sunday anyway I took an earlier train to attend the &lt;a href=&quot;http://berlinbuzzwords.de/wiki/barcamp&quot;&gt;barcamp&lt;/a&gt; in the early evening. It started with a short introduction of the concepts and the scheduling. Participants could suggest topics that they either would be willing to introduce by themselfes or just anything they are interested in. There were three roomes prepared, a larger and two smaller ones.&lt;/p&gt; &lt;p&gt;Among others I attended sessions on HBase, designing hybrid applications, Apache Tika and Apache Jackrabbit Oak.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://hbase.apache.org/&quot;&gt;HBase&lt;/a&gt; is a distributed database build on top of the Hadoop filesystem. It seems to be used more often than I would have expected. Interesting to hear about the problems and solutions of other people.&lt;/p&gt; &lt;p&gt;The next session on hybrid relational and NoSQL applications stayed rather high level. I liked the remark by one guy that &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Solr&lt;/a&gt;, the underdog of NoSQL, often is the first application where people are ok with dismissing some guarantees regarding their data. Adding NoSQL should be exactly like this.&lt;/p&gt; &lt;p&gt;I only &lt;a href=&quot;http://blog.florian-hopf.de/2012/05/content-extraction-with-apache-tika.html&quot;&gt;started just recently&lt;/a&gt; to use &lt;a href=&quot;http://tika.apache.org/&quot;&gt;Tika&lt;/a&gt; directly so it was really interesting to see where the project is heading in the future. I was surprised to hear that there now also is a TikaServer that can do similar things like those I described for Solr. That's something I want to try in action.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://wiki.apache.org/jackrabbit/Jackrabbit%203&quot;&gt;Jackrabbit Oak&lt;/a&gt; is a next generation content repository that is mostly driven by the Day team of Adobe. Some of the ideas sound really interesting but I got the feeling that it still can take some time until this really can be used. Jukka Zitting also gave a lightning talk on this topic at the conference, the &lt;a href=&quot;http://people.apache.org/~jukka/2012/Jackrabbit%20Oak.pdf&quot;&gt;slides are available here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;The atmosphere in the sessions was really relaxed so even though I expected to only listen I took the chance to participate and ask some questions. This probably is the part that makes a barcamp as effective as it is. As you are constantly participating you keep really contentrated on the topic.&lt;/p&gt; &lt;h4&gt;Day 1&lt;/h4&gt; &lt;p&gt;The first day started with a great keynote by &lt;a href=&quot;http://hawthornlandings.org/&quot;&gt;Leslie Hawthorn&lt;/a&gt; on building and maintaining communities. She compared a lot of the aspects of community work with gardening and introduced &lt;a href=&quot;http://openmrs.org/&quot;&gt;OpenMRS&lt;/a&gt;, a successful project building a medical record platform. Though I currently am not actively involved in an open source project I could relate to a lot of the situations she described. All in all an inspiring start of the main conference.&lt;/p&gt; &lt;p&gt;Next I attended a talk on building hybrid applications with &lt;a href=&quot;http://www.mongodb.org/&quot;&gt;MongoDb&lt;/a&gt;. Nothing new for me but I am glad that a lot of people now recommend to split monolithic applications into smaller services. This also is a way to experiment with different languages and techniques without having to migrate large parts of an application.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://berlinbuzzwords.de/sessions/jcr-view-world-everything-content-everything-tree&quot;&gt;A JCR view of the world&lt;/a&gt; provided some examples on how to model different structures using a content tree. Though really introductionary it was interesting to see what kind of applications can be build using a content repository. I also liked the attitude of the speaker: The presentation was delivered using &lt;a href=&quot;http://sling.apache.org/site/index.html&quot;&gt;Apache Sling&lt;/a&gt; which uses JCR under the hood.&lt;/p&gt; &lt;p&gt;Probably the highlight of the first day was the talk by Grant Ingersoll on &lt;a href=&quot;http://berlinbuzzwords.de/sessions/large-scale-search-discovery-and-analytics-hadoop-mahout-and-solr&quot;&gt;Large Scale Search, Discovery and Analytics&lt;/a&gt;. He introduced all the parts that make up larger search systems and showed the open source tools he uses. To increase the relevance of the search results you have to integrate solutions to adapt to the behaviour of the users. That's probably one of the big takeaways for me of the whole conference: Always collect data on your users searches to have it available when you want to tune the relevance, either manually or through some learning techniques. &lt;a href=&quot;http://www.slideshare.net/gsingers/large-scale-search-discovery-and-analytics-with-hadoop-mahout-and-solr-13203456&quot;&gt;The slides of the talk&lt;/a&gt; are worth looking at.&lt;/p&gt; &lt;p&gt;The rest of the day I attended several talks on the internals of &lt;a href=&quot;http://lucene.apache.org/&quot;&gt;Lucene&lt;/a&gt;. Hardcore stuff, I would be lying if I said I would have understood everything but it was interesting nevertheless. I am glad that some really smart people are taking care that Lucene stays as fast and feature rich as it is.&lt;/p&gt; &lt;p&gt;The day ended with interesting discussions and some beer at the Buzz Party and BBQ.&lt;/p&gt; &lt;h4&gt;Day 2&lt;/h4&gt; &lt;p&gt;The first talk of the second day on &lt;a href=&quot;http://berlinbuzzwords.de/sessions/smart-autocompl&quot;&gt;Smart Autocompl...&lt;/a&gt; by Anne Veling was fantastic. Anne demonstrated a rather simple technique for doing semantic analysis of search queries for specialized autocompletion for the largest travel information system in the Netherlands. The query gets tokenized and then each field of the index (e.g. street or city) is queried for each of the tokens. This way you can already guess which might be good field matches.&lt;/p&gt;   &lt;p&gt;Another talk introduced a scalable tool for preprocessing of documents, &lt;a href=&quot;http://www.findwise.com/hydra&quot;&gt;Hydra&lt;/a&gt;. It stores the documents as well as mapping data in a MongoDb instance and you can parallelize the processing steps. The concept sounds really interesting, I hope I can find time to have a closer look.&lt;/p&gt; &lt;p&gt;In the afternoon I attended several talks on &lt;a href=&quot;http://www.elasticsearch.org/&quot;&gt;Elasticsearch&lt;/a&gt;, the scalable search server. Interestingly a lot of people seem to use it more as a storage engine than for searching.&lt;/p&gt; &lt;p&gt;One of the tracks was cancelled, &lt;a href=&quot;http://tdunning.blogspot.de/&quot;&gt;Ted Dunning&lt;/a&gt; introduced new stuff in &lt;a href=&quot;http://mahout.apache.org/&quot;&gt;Mahout&lt;/a&gt; instead. He's a really funny speaker and though I am not deep into machine learning I was glad to hear that you are allowed to use and even contribute to Mahout even if you don't have a PhD.&lt;/p&gt;   &lt;p&gt;In the last track of the day Alex Pinkin showed 10 problems and solutions that you might encounter when building a large app using Solr. Quite some useful advice.&lt;/p&gt; &lt;h4&gt;The location&lt;/h4&gt; &lt;p&gt;The event took place at &lt;a href=&quot;http://www.urania.de/&quot;&gt;Urania&lt;/a&gt;, a smaller conference center and theatre. Mostly it was suited well but some of the talks were so full that you either had to sit on the floor or weren't even able to enter the room. I understand that it is difficult to predict how many people attend a certain event but some talks probably should have been scheduled in different rooms.&lt;/p&gt; &lt;p&gt;The food was really good and though it first looked like the distribution was a bottleneck this worked pretty well.&lt;/p&gt; &lt;h4&gt;The format&lt;/h4&gt; &lt;p&gt;This year Berlin Buzzwords had a rather unusual format. Most of the talks were only 20 minutes long with some exceptions that were 40 minutes long. I have mixed feelings about this: On the one hand it was great to have a lot of different topics. On the other hand some of the concepts definitively would have needed more time to fully explain and grasp. Respect to all the speakers who had to think about what they would talk about in such a short timeframe.&lt;/p&gt; &lt;p&gt;Berlin Buzzwords is a fantastic conference and I will definitively go there again.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Content Extraction with Apache Tika</title>
   <link href="http://blog.florian-hopf.de/2012/05/content-extraction-with-apache-tika.html"/>
   <updated>2012-05-12T01:58:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/05/content-extraction-with-apache-tika</id>
   <content type="html">&lt;p&gt;Sometimes you need access to the content of documents, be it that you want to analyze it, store the content in a database or index it for searching. Different formats like word documents, pdfs and html documents need different treatment. &lt;a href=&quot;http://tika.apache.org/&quot; target=&quot;_blank&quot;&gt;Apache Tika&lt;/a&gt; is a project that combines several open source projects for reading content from &lt;a href=&quot;http://tika.apache.org/1.1/formats.html&quot; target=&quot;_blank&quot;&gt;a multitude of file formats&lt;/a&gt; and makes the textual content as well as some metadata available using a uniform API. I will show two ways how to leverage the power of Tika for your projects.&lt;/p&gt;&lt;h4&gt;Accessing Tika programmatically&lt;/h4&gt;&lt;p&gt;First, Tika can of course be used as a library. Surprisingly the user docs on the website explain a lot of the functionality that you might be interested in when writing custom parsers for Tika but don't show directly how to use it.&lt;/p&gt;&lt;p&gt;I am using Maven again, so I add a dependency for the most recent version:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;groupId&amp;gt;org.apache.tika&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;artifactId&amp;gt;tika-parsers&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;version&amp;gt;1.1&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;tika-parsers&lt;/em&gt; also includes all the other projects that are used so be patient when Maven fetches all the transitive dependencies.&lt;/p&gt; &lt;p&gt;Let's see what some test code for extracting data from a pdf document called &lt;em&gt;slides.pdf&lt;/em&gt;, that is available in the classpath, looks like.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Parser parser = new PdfParser();&lt;br /&gt;BodyContentHandler handler = new BodyContentHandler();&lt;br /&gt;Metadata metadata = new Metadata();&lt;br /&gt;InputStream content = getClass().getResourceAsStream(&quot;/slides.pdf&quot;);&lt;br /&gt;parser.parse(content, handler, metadata, new ParseContext());&lt;br /&gt;assertEquals(&quot;Solr Vortrag&quot;, metadata.get(Metadata.TITLE));&lt;br /&gt;assertTrue(handler.toString().contains(&quot;Lucene&quot;));&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;First, we need to instanciate a &lt;a href=&quot;http://tika.apache.org/1.1/parser.html&quot; target=&quot;_blank&quot;&gt;Parser&lt;/a&gt; that is capable of reading the format, in this case PdfParser that uses &lt;a href=&quot;http://pdfbox.apache.org/&quot; target=&quot;_blank&quot;&gt;PDFBox&lt;/a&gt; for extracting the content. The parse method expects some parameters to configure the parsing process as well as an InputStream that contains the data of the document. Metadata will contain all the metadata for the document, e.g. the title or the author after the parsing is finished. &lt;/p&gt;&lt;p&gt;Tika uses XHTML as the internal representation for all parsed content. This XHTML document can be processed by a SAX ContentHandler. A custom implementation BodyContentHandler returns all the text in the body area, which is the main content. The last parameter ParseContext can be used to configure the underlying parser instance. &lt;/p&gt;&lt;p&gt;The Metadata class consists of a Map-like structure with some common keys like the title as well as optional format specific information. You can look at the contents with a simple loop:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;for (String name: metadata.names()) { &lt;br /&gt;    System.out.println(name + &quot;: &quot; + metadata.get(name));&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will produce an output similar to this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;xmpTPg:NPages: 17&lt;br /&gt;Creation-Date: 2010-11-20T09:47:28Z&lt;br /&gt;title: Solr Vortrag&lt;br /&gt;created: Sat Nov 20 10:47:28 CET 2010&lt;br /&gt;producer: OpenOffice.org 2.4&lt;br /&gt;Content-Type: application/pdf&lt;br /&gt;creator: Impress&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The textual content of the document can be retrieved by calling the toString() method on the BodyContentHandler.&lt;/p&gt;&lt;p&gt;This is all fine if you exactly know that you only want to retrieve data from pdf documents. But you probably don't want to introduce a huge switch-block for determining the parser to use depending on the file name or some other information. Fortunately Tika also provides an AutodetectParser that employs different strategies for determining the content type of the document. The code above all stays the same, you just use a different parser:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Parser parser = new AutodetectParser();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way you don't have to know what kind of document you are currently processing, Tika will provide you with metadata as well as the content. You can pass in additional hints for the parser e.g. the filename or the content type by setting it in the Metadata object.&lt;/p&gt;&lt;h4&gt;Extracting content using Solr&lt;/h4&gt;&lt;p&gt;If you are using &lt;a href=&quot;http://lucene.apache.org/solr/&quot; target=&quot;_blank&quot;&gt;the search server Solr&lt;/a&gt; you can also leverage its REST API for extracting the content. The default configuration has a &lt;a href=&quot;http://wiki.apache.org/solr/ExtractingRequestHandler&quot; target=&quot;_blank&quot;&gt;request handler&lt;/a&gt; configured for &lt;em&gt;/update/extract&lt;/em&gt; that you can send a document to and it will return the content it extracted using Tika. You just need to add the necessary libraries for the extraction. I am still using Maven so I have to add an additional dependency:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;groupId&amp;gt;org.apache.solr&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;artifactId&amp;gt;solr&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;version&amp;gt;3.6.0&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;type&amp;gt;war&amp;lt;/type&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;groupId&amp;gt;org.apache.solr&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;artifactId&amp;gt;solr-cell&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;version&amp;gt;3.6.0&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will include all of the Tika dependencies as well as all necessary third party libraries.&lt;/p&gt;&lt;p&gt;Solr Cell, the request handler, normally is used to index binary files directly but you can also just use it for extraction. To transfer the content you can use any tool that can speak http, e.g. for curl this might look like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;curl -F &quot;file=@slides.pdf&quot; &quot;localhost:8983/solr/update/extract?extractOnly=true&amp;amp;extractFormat=text&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By setting the parameter &lt;em&gt;extractOnly&lt;/em&gt; to true we advice Solr that we don't want to index the content but want to have it extracted to the response. The result will be the standard Solr XML format that contains the body content as well as the metadata.&lt;/p&gt;&lt;p&gt;You can also use the Java client library &lt;a href=&quot;http://wiki.apache.org/solr/Solrj&quot; target=&quot;_blank&quot;&gt;SolrJ&lt;/a&gt; for doing the same:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ContentStreamUpdateRequest request = new ContentStreamUpdateRequest(&amp;quot;/update/extract&amp;quot;);&lt;br /&gt;request.addFile(new File(&amp;quot;slides.pdf&amp;quot;));&lt;br /&gt;request.setParam(&amp;quot;extractOnly&amp;quot;, &amp;quot;true&amp;quot;);&lt;br /&gt;request.setParam(&amp;quot;extractFormat&amp;quot;, &amp;quot;text&amp;quot;);&lt;br /&gt;NamedList&amp;lt;Object&amp;gt; result = server.request(request);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;em&gt;NamedList&lt;/em&gt; will contain entries for the body content as well as another &lt;em&gt;NamedList&lt;/em&gt; with the metadata.&lt;/p&gt;&lt;br /&gt;&lt;h4&gt;Update&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Robert has asked in the comments what the response looks like.&lt;br /&gt;Solr uses configurable response writers for marshalling the message. The default format is xml but can be influenced by passing the wt attribute to the request. A simplified standard response looks like this:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;curl -F &amp;quot;file=@slides.pdf&amp;quot; &amp;quot;localhost:8983/solr/update/extract?extractOnly=true&amp;amp;extractFormat=text&amp;quot;&lt;br /&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;response&amp;gt;&lt;br /&gt;&amp;lt;lst name=&amp;quot;responseHeader&amp;quot;&amp;gt;&amp;lt;int name=&amp;quot;status&amp;quot;&amp;gt;0&amp;lt;/int&amp;gt;&amp;lt;int name=&amp;quot;QTime&amp;quot;&amp;gt;1952&amp;lt;/int&amp;gt;&amp;lt;/lst&amp;gt;&amp;lt;str name=&amp;quot;slides.pdf&amp;quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;Features                                                                                                                                                                            &lt;br /&gt;                                                                                                                                                                                    &lt;br /&gt;HTTP&amp;#173;Schnittstelle                                                                                                                                                                  &lt;br /&gt;XML&amp;#173;basierte&amp;#160;Konfiguration                                                                                                                                                          &lt;br /&gt;Facettierung                                                                                                                                                                        &lt;br /&gt;Sammlung&amp;#160;n&amp;#252;tzlicher&amp;#160;Lucene&amp;#173;Module/Dismax                                                                                                                                            &lt;br /&gt;                                                                                                                                                                                    &lt;br /&gt;Features                                                                                                                                                                            &lt;br /&gt;                                                                                                                                                                                    &lt;br /&gt;HTTP&amp;#173;Schnittstelle                                                                                                                                                                  &lt;br /&gt;XML&amp;#173;basierte&amp;#160;Konfiguration                                                                                                                                                          &lt;br /&gt;Facettierung                                                                                                                                                                        &lt;br /&gt;Sammlung&amp;#160;n&amp;#252;tzlicher&amp;#160;Lucene&amp;#173;Module/Dismax                                                                                                                                            &lt;br /&gt;Java&amp;#173;Client&amp;#160;SolrJ                                                                                                                                                                   &lt;br /&gt;                                                                                                                                                                                    &lt;br /&gt;[... more content ...] &lt;br /&gt;                                                                                                                                                                                   &lt;br /&gt;&amp;lt;/str&amp;gt;&amp;lt;lst name=&amp;quot;slides.pdf_metadata&amp;quot;&amp;gt;&amp;lt;arr name=&amp;quot;xmpTPg:NPages&amp;quot;&amp;gt;&amp;lt;str&amp;gt;17&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;Creation-Date&amp;quot;&amp;gt;&amp;lt;str&amp;gt;2010-11-20T09:47:28Z&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;title&amp;quot;&amp;gt;&amp;lt;str&amp;gt;Solr Vortrag&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;stream_source_info&amp;quot;&amp;gt;&amp;lt;str&amp;gt;file&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;created&amp;quot;&amp;gt;&amp;lt;str&amp;gt;Sat Nov 20 10:47:28 CET 2010&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;stream_content_type&amp;quot;&amp;gt;&amp;lt;str&amp;gt;application/octet-stream&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;stream_size&amp;quot;&amp;gt;&amp;lt;str&amp;gt;425327&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;producer&amp;quot;&amp;gt;&amp;lt;str&amp;gt;OpenOffice.org 2.4&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;stream_name&amp;quot;&amp;gt;&amp;lt;str&amp;gt;slides.pdf&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;Content-Type&amp;quot;&amp;gt;&amp;lt;str&amp;gt;application/pdf&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;arr name=&amp;quot;creator&amp;quot;&amp;gt;&amp;lt;str&amp;gt;Impress&amp;lt;/str&amp;gt;&amp;lt;/arr&amp;gt;&amp;lt;/lst&amp;gt;                                                                            &lt;br /&gt;&amp;lt;/response&amp;gt;     &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The response contains some metadata (how long the processing took), the content of the file as well as the metadata that is extracted from the document.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;If you pass the atrribute &lt;em&gt;wt&lt;/em&gt; and set it to &lt;em&gt;json&lt;/em&gt;, the response is contained in a json structure:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;curl -F &amp;quot;file=@slides.pdf&amp;quot; &amp;quot;localhost:8983/solr/update/extract?extractOnly=true&amp;amp;extractFormat=text&amp;amp;wt=json&amp;quot;             &lt;br /&gt;{&amp;quot;responseHeader&amp;quot;:{&amp;quot;status&amp;quot;:0,&amp;quot;QTime&amp;quot;:217},&amp;quot;slides.pdf&amp;quot;:&amp;quot;\n\n\n\n\n\n\n\n\n\n\n\nSolr Vortrag\n\n&amp;#160; &amp;#160;\n\nEinfach mehr finden mit\n\nFlorian&amp;#160;Hopf\n29.09.2010\n\n\n&amp;#160; &amp;#160;\n\nSolr?\n\n\n&amp;#160; &amp;#160;\n\nSolr?\n\nServer&amp;#173;ization&amp;#160;of&amp;#160;Lucene\n\n\n&amp;#160; &amp;#160;\n\nApache Lucene?\n\nSearch&amp;#160;engine&amp;#160;library\n\n\n&amp;#160; &amp;#160;\n\nApache Lucene?\n\nSearch&amp;#160;engine&amp;#160;library\nTextbasierter&amp;#160;Index\n\n\n&amp;#160; &amp;#160;\n\nApache Lucene?\n\nSearch&amp;#160;engine&amp;#160;library\nTextbasierter&amp;#160;Index\nText&amp;#160;Analyzer\n\n\n&amp;#160; &amp;#160;\n\nApache Lucene?\n\nSearch&amp;#160;engine&amp;#160;library\nTextbasierter&amp;#160;Index\nText&amp;#160;Analyzer\nQuery&amp;#160;Syntax&amp;#160;\n\n\n&amp;#160; &amp;#160;\n\nApache Lucene?\n\nSearch&amp;#160;engine&amp;#160;library\nTextbasierter&amp;#160;Index\nText&amp;#160;Analyzer\nQuery&amp;#160;Syntax&amp;#160;\nScoring\n\n\n&amp;#160; &amp;#160;\n\nFeatures\n\nHTTP&amp;#173;Schnittstelle\n\n\n&amp;#160; &amp;#160;\n\nArchitektur\n\nClient SolrWebapp Lucene\nhttp\n\nKommunikation&amp;#160;&amp;#252;ber&amp;#160;XML,&amp;#160;JSON,&amp;#160;JavaBin,&amp;#160;Ruby,&amp;#160;...\n\n\n&amp;#160; &amp;#160;\n\nFeatures\n\nHTTP&amp;#173;Schnittstelle\nXML&amp;#173;basierte&amp;#160;Konfiguration\n\n\n&amp;#160; &amp;#160;\n\nFeatures\n\nHTTP&amp;#173;Schnittstelle\nXML&amp;#173;basierte&amp;#160;Konfiguration\nFacettierung\n\n\n&amp;#160; &amp;#160;\n\nFeatures\n\nHTTP&amp;#173;Schnittstelle\nXML&amp;#173;basierte&amp;#160;Konfiguration\nFacettierung\nSammlung&amp;#160;n&amp;#252;tzlicher&amp;#160;Lucene&amp;#173;Module/Dismax\n\n\n&amp;#160; &amp;#160;\n\nFeatures\n\nHTTP&amp;#173;Schnittstelle\nXML&amp;#173;basierte&amp;#160;Konfiguration\nFacettierung\nSammlung&amp;#160;n&amp;#252;tzlicher&amp;#160;Lucene&amp;#173;Module/Dismax\nJava&amp;#173;Client&amp;#160;SolrJ\n\n\n&amp;#160; &amp;#160;\n\nDemo\n\n\n&amp;#160; &amp;#160;\n\nWas noch?\nAdmin&amp;#173;Interface\nCaching\nSkalierung\nSpellchecker\nMore&amp;#173;Like&amp;#173;This\nData&amp;#160;Import&amp;#160;Handler\nSolrCell\n\n\n&amp;#160; &amp;#160;\n\nRessourcen\nhttp://lucene.apache.org/solr/\n\n\n\n&amp;quot;,&amp;quot;slides.pdf_metadata&amp;quot;:[&amp;quot;xmpTPg:NPages&amp;quot;,[&amp;quot;17&amp;quot;],&amp;quot;Creation-Date&amp;quot;,[&amp;quot;2010-11-20T09:47:28Z&amp;quot;],&amp;quot;title&amp;quot;,[&amp;quot;Solr Vortrag&amp;quot;],&amp;quot;stream_source_info&amp;quot;,[&amp;quot;file&amp;quot;],&amp;quot;created&amp;quot;,[&amp;quot;Sat Nov 20 10:47:28 CET 2010&amp;quot;],&amp;quot;stream_content_type&amp;quot;,[&amp;quot;application/octet-stream&amp;quot;],&amp;quot;stream_size&amp;quot;,[&amp;quot;425327&amp;quot;],&amp;quot;producer&amp;quot;,[&amp;quot;OpenOffice.org 2.4&amp;quot;],&amp;quot;stream_name&amp;quot;,[&amp;quot;slides.pdf&amp;quot;],&amp;quot;Content-Type&amp;quot;,[&amp;quot;application/pdf&amp;quot;],&amp;quot;creator&amp;quot;,[&amp;quot;Impress&amp;quot;]]}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;There are quite some ResponseWriters available for different languages, e.g. for Ruby. You can have a look at them at the bottom of this page: &lt;a href=&quot;http://wiki.apache.org/solr/QueryResponseWriter&quot;&gt;http://wiki.apache.org/solr/QueryResponseWriter&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Importing Atom feeds in Solr using the Data Import Handler</title>
   <link href="http://blog.florian-hopf.de/2012/05/importing-atom-feeds-in-solr-using-data.html"/>
   <updated>2012-05-08T00:58:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/05/importing-atom-feeds-in-solr-using-data</id>
   <content type="html">&lt;p&gt;I am working on a search solution that makes some of the content I am producing available through one search interface. One of the content stores is &lt;a href=&quot;http://fhopf.blogspot.com&quot;&gt;the blog&lt;/a&gt; you are reading right now, which among other options makes the content available &lt;a href=&quot;http://fhopf.blogspot.com/feeds/posts/default?max-results=100&quot;&gt;here&lt;/a&gt; using &lt;a href=&quot;http://en.wikipedia.org/wiki/Atom_%28standard%29&quot;&gt;Atom&lt;/a&gt;.&lt;/p&gt;   &lt;p&gt;&lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Solr&lt;/a&gt;, my search server of choice, provides the &lt;a href=&quot;http://wiki.apache.org/solr/DataImportHandler&quot;&gt;Data Import Handler&lt;/a&gt; that can be used to import data on a regular basis from sources like databases via JDBC or remote XML sources, like Atom.&lt;/p&gt; &lt;p&gt;Data Import Handler used to be a core part of Solr but &lt;a href=&quot;http://www.lucidimagination.com/blog/2011/04/01/solr-powered-isfdb-part-8/&quot;&gt;starting from 3.1&lt;/a&gt; it is shipped as a separate jar and not included in the standard war anymore. I am using &lt;a href=&quot;http://maven.apache.org&quot;&gt;Maven&lt;/a&gt; with overlays for development so I have to add a dependency for it:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;dependency&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;groupId&amp;gt;org.apache.solr&amp;lt;/groupId&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;artifactId&amp;gt;solr&amp;lt;/artifactId&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;version&amp;gt;3.6.0&amp;lt;/version&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;type&amp;gt;war&amp;lt;/type&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/dependency&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;dependency&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;groupId&amp;gt;org.apache.solr&amp;lt;/groupId&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;artifactId&amp;gt;solr-dataimporthandler&amp;lt;/artifactId&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;version&amp;gt;3.6.0&amp;lt;/version&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/dependency&amp;gt;&lt;br/&gt;&amp;lt;/dependencies&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;To enable the data import handler you have to add a request handler to your &lt;em&gt;solrconfig.xml&lt;/em&gt;. Request handlers are registered for a certain url and, as the name suggests, are responsible for handling incoming requests:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;requestHandler&amp;nbsp;name=&amp;quot;/dataimport&amp;quot;&amp;nbsp;class=&amp;quot;org.apache.solr.handler.dataimport.DataImportHandler&amp;quot;&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;lst&amp;nbsp;name=&amp;quot;defaults&amp;quot;&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;str&amp;nbsp;name=&amp;quot;config&amp;quot;&amp;gt;data-config.xml&amp;lt;/str&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/lst&amp;gt;&lt;br/&gt;&amp;lt;/requestHandler&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The file &lt;em&gt;data-config.xml&lt;/em&gt; that is referenced here contains the mapping logic as well as the endpoint to access:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;?xml&amp;nbsp;version=&amp;quot;1.0&amp;quot;&amp;nbsp;encoding=&amp;quot;UTF-8&amp;quot;&amp;nbsp;?&amp;gt;&lt;br/&gt;&amp;lt;dataConfig&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;dataSource&amp;nbsp;type=&amp;quot;URLDataSource&amp;quot;&amp;nbsp;encoding=&amp;quot;UTF-8&amp;quot;&amp;nbsp;connectionTimeout=&amp;quot;5000&amp;quot;&amp;nbsp;readTimeout=&amp;quot;10000&amp;quot;/&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;document&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;entity&amp;nbsp;name=&amp;quot;blog&amp;quot;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pk=&amp;quot;url&amp;quot;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url=&amp;quot;http://fhopf.blogspot.com/feeds/posts/default?max-results=100&amp;quot;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;processor=&amp;quot;XPathEntityProcessor&amp;quot;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;forEach=&amp;quot;/feed/entry&amp;quot;&amp;nbsp;transformer=&amp;quot;DateFormatTransformer,HTMLStripTransformer,TemplateTransformer&amp;quot;&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;field&amp;nbsp;column=&amp;quot;title&amp;quot;&amp;nbsp;xpath=&amp;quot;/feed/entry/title&amp;quot;/&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;field&amp;nbsp;column=&amp;quot;url&amp;quot;&amp;nbsp;xpath=&amp;quot;/feed/entry/link[@rel='alternate']/@href&amp;quot;/&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!--&amp;nbsp;2012-03-07T21:35:51.229-08:00&amp;nbsp;--&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;field&amp;nbsp;column=&amp;quot;last_modified&amp;quot;&amp;nbsp;xpath=&amp;quot;/feed/entry/updated&amp;quot;&amp;nbsp;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dateTimeFormat=&amp;quot;yyyy-MM-dd'T'hh:mm:ss.SSS&amp;quot;&amp;nbsp;locale=&amp;quot;en&amp;quot;/&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;field&amp;nbsp;column=&amp;quot;text&amp;quot;&amp;nbsp;xpath=&amp;quot;/feed/entry/content&amp;quot;&amp;nbsp;stripHTML=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;field&amp;nbsp;column=&amp;quot;category&amp;quot;&amp;nbsp;xpath=&amp;quot;/feed/entry/category/@term&amp;quot;/&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;field&amp;nbsp;column=&amp;quot;type&amp;quot;&amp;nbsp;template=&amp;quot;blog&amp;quot;/&amp;gt;&amp;nbsp;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/entity&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/document&amp;gt;&lt;br/&gt;&amp;lt;/dataConfig&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;First we configure which &lt;em&gt;datasource&lt;/em&gt; to use. This is where you alternatively would use another implementation when fetching documents from a database.&lt;/p&gt; &lt;p&gt;Documents describe the fields that will be stored in the index. The attributes for the &lt;em&gt;entity&lt;/em&gt; element determine where and how to fetch the data, most importantly the &lt;em&gt;url&lt;/em&gt; and the &lt;em&gt;processor&lt;/em&gt;. &lt;em&gt;forEach&lt;/em&gt; contains an XPath to identify the elements we'd like to loop over. The &lt;em&gt;transformer&lt;/em&gt; attribute is used to specify some classes that are the available when mapping the remote XML to the Solr fields.&lt;/p&gt; &lt;p&gt;The &lt;em&gt;field&lt;/em&gt; elements contain the mapping between the Atom document and the Solr index fields. The &lt;em&gt;column&lt;/em&gt; attribute determines the name of the index field, &lt;em&gt;xpath&lt;/em&gt; determines the node to use in the remote XML document. You can use advanced XPath options like mapping to attributes of elements where only another attribute is set. E.g. &lt;em&gt;/feed/entry/link[@rel='alternate']/@href&lt;/em&gt; points to an element that determines an alternative representation of a blog post entry:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;feed&amp;nbsp;...&amp;gt;&amp;nbsp;&lt;br/&gt;&amp;nbsp;&amp;nbsp;...&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;entry&amp;gt;&amp;nbsp;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;link&amp;nbsp;rel='alternate'&amp;nbsp;type='text/html'&amp;nbsp;href='http://fhopf.blogspot.com/2012/03/testing-akka-actors-from-java.html'&amp;nbsp;title='Testing&amp;nbsp;Akka&amp;nbsp;actors&amp;nbsp;from&amp;nbsp;Java'/&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/entry&amp;gt;&lt;br/&gt;...&lt;br/&gt;&amp;lt;/feed&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;For the column &lt;em&gt;last_modified&lt;/em&gt; we are transforming the remote date format to the internal Solr representation using the DateProcessor. I am not sure yet if this is the correct solution as it seems to me I'm losing the timezone information. For the &lt;em&gt;text&lt;/em&gt; field we are first removing all html elements that are contained in the blog post using the HTMLStripTransformer. Finally, the &lt;em&gt;type&lt;/em&gt; contains a hardcoded value that is set using the TemplateTransformer.&lt;/p&gt; &lt;p&gt;To have everything in one place let's see how the schema for our index looks like:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;field&amp;nbsp;name=&amp;quot;url&amp;quot;&amp;nbsp;type=&amp;quot;string&amp;quot;&amp;nbsp;indexed=&amp;quot;true&amp;quot;&amp;nbsp;stored=&amp;quot;true&amp;quot;&amp;nbsp;required=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;&amp;lt;field&amp;nbsp;name=&amp;quot;title&amp;quot;&amp;nbsp;type=&amp;quot;text_general&amp;quot;&amp;nbsp;indexed=&amp;quot;true&amp;quot;&amp;nbsp;stored=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;&amp;lt;field&amp;nbsp;name=&amp;quot;category&amp;quot;&amp;nbsp;type=&amp;quot;text_general&amp;quot;&amp;nbsp;indexed=&amp;quot;true&amp;quot;&amp;nbsp;stored=&amp;quot;true&amp;quot;&amp;nbsp;multiValued=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;&amp;lt;field&amp;nbsp;name=&amp;quot;last_modified&amp;quot;&amp;nbsp;type=&amp;quot;date&amp;quot;&amp;nbsp;indexed=&amp;quot;true&amp;quot;&amp;nbsp;stored=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;&amp;lt;field&amp;nbsp;name=&amp;quot;text&amp;quot;&amp;nbsp;type=&amp;quot;text_general&amp;quot;&amp;nbsp;indexed=&amp;quot;true&amp;quot;&amp;nbsp;stored=&amp;quot;false&amp;quot;&amp;nbsp;multiValued=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;&amp;lt;field&amp;nbsp;name=&amp;quot;type&amp;quot;&amp;nbsp;type=&amp;quot;string&amp;quot;&amp;nbsp;indexed=&amp;quot;true&amp;quot;&amp;nbsp;stored=&amp;quot;false&amp;quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Finally, how can you trigger the dataimport? There is an option described in the &lt;a href=&quot;http://wiki.apache.org/solr/DataImportHandler#Scheduling&quot;&gt;Solr wiki&lt;/a&gt;, but probably a simple solution might be enough for you. I am using a shell script that is triggered by a cron job. These are the contents:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;curl localhost:8983/solr/dataimport?command=full-import&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The data import handler is really easy to setup and you can use it to import quite a lot of data sources into your index. If you need more advanced crawling features you might want to have a look at &lt;a href=&quot;http://incubator.apache.org/connectors/&quot;&gt;Apache ManifoldCF&lt;/a&gt;, a connector framework for plugging content repositories into search engines like Apache Solr.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Testing Akka actors from Java</title>
   <link href="http://blog.florian-hopf.de/2012/03/testing-akka-actors-from-java.html"/>
   <updated>2012-03-07T17:02:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/03/testing-akka-actors-from-java</id>
   <content type="html">&lt;p&gt;&lt;em&gt;If you're looking for a general introduction into using Akka from Java have a look at &lt;a href=&quot;http://blog.florian-hopf.de/2012/08/getting-rid-of-synchronized-using-akka.html&quot;&gt;this post&lt;/a&gt;&lt;/em&gt;&lt;/p&gt; &lt;p&gt;In a recent project I've been using &lt;a href=&quot;http://akka.io/&quot;&gt;Akka&lt;/a&gt; for a concurrent producer-consumer setup. It is an actor framework for the JVM that is implemented in Scala but provides a Java API so normally you don't notice that your dealing with a Scala library. &lt;/p&gt; &lt;p&gt;Most of my business code is encapsulated in services that don't depend on Akka and can therefore be tested in isolation. But for some cases I've been looking for a way to test the behaviour of the actors. As I struggled with this for a while and didn't find a real howto on testing Akka actors from Java I hope my notes might be useful for other people as well.&lt;/p&gt; &lt;p&gt;The main problem when testing actors is that they are managed objects and you can't just instanciate them. Akka comes with a module for tests that is documented well for &lt;a href=&quot;http://akka.io/docs/akka/2.0/scala/testing.html&quot;&gt;using it from Scala&lt;/a&gt;. But besides the note that it's possible you don't find a lot of information on using it from Java.&lt;/p&gt; &lt;p&gt;When using Maven you need to make sure that you have the akka-testkit dependency in place:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;    &amp;lt;groupId&amp;gt;com.typesafe.akka&amp;lt;/groupId&amp;gt;&lt;br /&gt;    &amp;lt;artifactId&amp;gt;akka-testkit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;    &amp;lt;version&amp;gt;2.1-SNAPSHOT&amp;lt;/version&amp;gt;&lt;br /&gt;    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;I will show you how to implement a test for the actors that are introduced in the &lt;a href=&quot;http://akka.io/docs/akka/2.0/intro/getting-started-first-java.html&quot;&gt;Akka java tutorial&lt;/a&gt;. It involves one actor that does a substep of calculating Pi for a certain start number and a given number of elements. &lt;/p&gt; &lt;p&gt;To test this actor we need a way to set it up. Akka-testkit provides a helper &lt;a href=&quot;http://akka.io/api/akka/2.0/akka/testkit/TestActorRef.html&quot;&gt;TestActorRef&lt;/a&gt; that can be used to set it up. Using scala this seems to be rather simple:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;val testActor = TestActorRef[Worker]&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;If you try to do this from Java you will notice that you can't use a similar call. I have to admit that I am not quite sure yet what is going on. I would have expected that there is an apply() method on the TestActorRef companion object that uses some kind of implicits to instanciate the Worker object. But when inspecting the sources the thing that comes closest to it is this definition:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;def apply[T &lt;: Actor](factory: ⇒ T)(implicit system: ActorSystem)&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;No sign of implicit for the factory. Something I still have to investigate further.&lt;/p&gt; &lt;p&gt;To use it from Java you can use the method apply that takes a reference to a &lt;a href=&quot;http://www.scala-lang.org/api/current/scala/Function0.html&quot;&gt;Function0&lt;/a&gt; and an actor system. The actor system can be setup easily using&lt;/p&gt; &lt;pre&gt;&lt;code&gt;actorSystem = ActorSystem.apply();&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The &lt;a href=&quot;http://stackoverflow.com/questions/1223834/how-does-scalas-apply-method-magic-work/1223913#1223913&quot;&gt;apply() method&lt;/a&gt; is very important in scala as it's kind of the default method for objects. For example myList(1) is internally using myList.apply(1).&lt;/p&gt; &lt;o&gt;If you're like me and expect that Function0 is a single method interface you will be surprised. It contains a lot of strange looking methods that you really don't want to have cluttering your test code:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;TestActorRef&lt;Pi.Worker&gt; workerRef = TestActorRef.apply(new Function0&lt;Pi.Worker&gt;() {&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public Worker apply() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void apply$mcV$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public boolean apply$mcZ$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public byte apply$mcB$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public short apply$mcS$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public char apply$mcC$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public int apply$mcI$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public long apply$mcJ$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public float apply$mcF$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public double apply$mcD$sp() {&lt;br /&gt;        throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);&lt;br /&gt;    }&lt;br /&gt;}, actorSystem);&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The only method we really are interested in is the normal apply method. Where do those other methods come from? There is no obvious hint in the scaladocs. &lt;/p&gt; &lt;p&gt;During searching for the solution I found a &lt;a href=&quot;http://scala-programming-language.1934581.n4.nabble.com/Problem-Java-and-Scala-interoperability-in-2-8-0-RC1-td2125847.html&quot;&gt;mailing list thread&lt;/a&gt; that explains some of the magic. The methods are performance optimizations for boxing and unboxing that are automatically generated by the scala compiler for the &lt;a href=&quot;http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/scala/specialized.html&quot;&gt;@specialized annotation&lt;/a&gt;. Still, I am unsure about why this is happening exactly. According to &lt;a href=&quot;http://days2010.scala-lang.org/node/138/151&quot;&gt;this presentation&lt;/a&gt; I would have expected that I am using the specialized instance for Object, maybe that is something special regarding traits? &lt;/p&gt; &lt;p&gt;Fortunately we don't really need to implement the interface ourself: There's an adapter class, &lt;a href=&quot;http://www.scala-lang.org/api/current/scala/runtime/AbstractFunction0.html&quot;&gt;AbstractFunction0&lt;/a&gt;, that makes your code look much nicer:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;@Before&lt;br /&gt;public void initActor() {&lt;br /&gt;    actorSystem = ActorSystem.apply();&lt;br /&gt;    actorRef = TestActorRef.apply(new AbstractFunction0&lt;Pi.Worker&gt;() {&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        public Pi.Worker apply() {&lt;br /&gt;            return new Pi.Worker();&lt;br /&gt;        }&lt;br /&gt;           &lt;br /&gt;    }, actorSystem);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;This is like I would have expected it to behave in the first place.&lt;/p&gt; &lt;p&gt;Now, as we have setup our test we can use the TestActorRef to really test the actor. For example we can test that the actor doesn't do anything for a String message:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;@Test&lt;br /&gt;public void doNothingForString() {&lt;br /&gt;    TestProbe testProbe = TestProbe.apply(actorSystem);&lt;br /&gt;    actorRef.tell(&quot;Hello&quot;, testProbe.ref());&lt;br /&gt;&lt;br /&gt;    testProbe.expectNoMsg(Duration.apply(100, TimeUnit.MILLISECONDS));&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;&lt;a href=&quot;http://akka.io/api/akka/2.0/akka/testkit/TestProbe.html&quot;&gt;TestProbe&lt;/a&gt; is another helper that can be used to check the messages that are sent between cooperating actors. In this example we are checking that no message is passed to the sender for 100 miliseconds, which should be enough for execution.&lt;/p&gt; &lt;p&gt;Let's test some real functionality. Send a message to the actor and check that the result message is send:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;@Test&lt;br /&gt;public void calculatePiFor0() {&lt;br /&gt;    TestProbe testProbe = TestProbe.apply(actorSystem);&lt;br /&gt;    Pi.Work work = new Pi.Work(0, 0);        &lt;br /&gt;    actorRef.tell(work, testProbe.ref());&lt;br /&gt;&lt;br /&gt;    testProbe.expectMsgClass(Pi.Result.class);     &lt;br /&gt;    TestActor.Message message = testProbe.lastMessage();&lt;br /&gt;    Pi.Result resultMsg = (Pi.Result) message.msg();&lt;br /&gt;    assertEquals(0.0, resultMsg.getValue(), 0.0000000001);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;   &lt;p&gt;Now we use the TestProbe to block until a message arrives. When it's there we can have a look at using the lastMessage().&lt;/p&gt; &lt;p&gt;You can look at the rest of the test on &lt;a href=&quot;https://github.com/fhopf/akka/tree/master/akka-tutorials/akka-tutorial-first&quot;&gt;Github&lt;/a&gt;. Comments are more than welcome as I am pretty new to Scala as well as Akka.&lt;/p&gt; &lt;h4&gt;Update&lt;/h4&gt;&lt;p&gt;As &lt;a href=&quot;https://twitter.com/#!/jboner/status/177488321784713216&quot;&gt;Jonas Bonér points out&lt;/a&gt; I've been using the Scala API. Using the Props class the setup is easier:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;    @Before&lt;br /&gt;    public void initActor() {&lt;br /&gt;        actorSystem = ActorSystem.apply();&lt;br /&gt;        actorRef = TestActorRef.apply(new Props(Pi.Worker.class), actorSystem);&lt;br /&gt;    }&lt;/code&gt;&lt;/pre&gt;</content>
 </entry>
 
 <entry>
   <title>Legacy Code Retreat</title>
   <link href="http://blog.florian-hopf.de/2012/02/legacy-code-retreat.html"/>
   <updated>2012-02-20T00:22:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/02/legacy-code-retreat</id>
   <content type="html">&lt;p&gt;Yesterday I attended the first german Legacy Code Retreat in Bretten. The event was organized by &lt;a href=&quot;http://groupspaces.com/softwerkskammer/&quot;&gt;Softwerkskammer&lt;/a&gt;, the german software craftsmanship community. &lt;/p&gt; &lt;p&gt;A &lt;a href=&quot;http://whatis.legacycoderetreat.com/&quot;&gt;legacy code retreat&lt;/a&gt; doesn't work like a common code retreat where you implement a certain functionality again and again. It instead starts with some really flawed code and the participants apply different refactoring steps to make it more testable and maintainable. There are six iterations of 45 minutes with different tasks or aims. For each iteration you work with a different partner and after a short retrospective with all participants you mostly start again from the original code. &lt;/p&gt; &lt;p&gt;The &lt;a href=&quot;https://github.com/jbrains/trivia&quot;&gt;github repository for the legacy code&lt;/a&gt; contains the code in several languages among which are Java, C++, C# and Ruby.&lt;/p&gt; &lt;h4&gt;Iteration 1&lt;/h4&gt; &lt;p&gt;The first iteration was used to get to know the functionality of the code. There were no real rules so the participants were free to explore the code in any way they liked.&lt;/p&gt; &lt;p&gt;I paired with &lt;a href=&quot;http://www.lesscode.de/&quot;&gt;Heiko Seebach&lt;/a&gt; who I already knew to be a Ruby guy. We were looking at the code with a standard text editor, already quite unfamiliar to standard Java IDE work. I already got enough Ruby knowledge to understand code when I see it so this was no problem. For quite some time we tried to understand a certain aspect that was happening when running the code. It turned out that this was a bug in the Ruby-version of the code. Next we tried to setup RSpec and get starting with some tests.&lt;/p&gt; &lt;p&gt;During this iteration I didn't learn that much about the legacy code but more about some Ruby stuff.&lt;/p&gt; &lt;h4&gt;Iteration 2&lt;/h4&gt; &lt;p&gt;The target of the second iteration was to prepare a golden master test that could be used during all of the following iterations. The original legacy code is triggered by random input (in the Java version using java.util.Random) and writes all its state to System.out. We should capture the output for a certain input sequence and write it to a file. This can then automatically be compared to the output of a modified version. If both files are the same there are likely no regresions in the code.&lt;/p&gt; &lt;p&gt;I paired with another Java guy and we were working on my machine in Netbeans. I noticed how unfamiliar I am with standard Netbeans project setup as I am using Maven most of the time. We were doing the test and started some refactorings, all in all a quite productive iteration. Things I learned: &lt;a href=&quot;http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Random.html&quot;&gt;java.util.Random&lt;/a&gt; really only uses the seed for its number generation so if you are using the same seed again and again you always get the same result. Also, when doing file stuff in plain Java I really miss &lt;a href=&quot;http://commons.apache.org/io/&quot;&gt;commons-io&lt;/a&gt;.&lt;/p&gt; &lt;h4&gt;Iteration 3&lt;/h4&gt; &lt;p&gt;In Iteration 3 we were supposed to use an antipattern for testing: &lt;a href=&quot;http://c2.com/cgi/wiki?SubclassToTestAntiPattern&quot;&gt;Subclass to Test&lt;/a&gt;. You take the original class and overwrite some methods in it that are called from the method to test.&lt;/p&gt; &lt;p&gt;It turned out that the original code is not suited well for this approach. There are only few methods that really rely on other methods. Most of the methods are accessing the state via the fields directly. Me and my partner therefore didn't really overwrite the methods but instead use an &lt;a href=&quot;http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html&quot;&gt;initializer block&lt;/a&gt; for prepareing the state of method calls. This is similar to an approach for Map-initialization that I started to apply only just recently:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;Map&lt;String, String&gt; data = new HashMap&lt;String, String&gt;() {&lt;br /&gt;    {&lt;br /&gt;        put(&quot;key&quot;, &quot;value&quot;);&lt;br /&gt;    }&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The approach worked quite fine for the given code but it's probably true that the tests won't stay maintainable.&lt;/p&gt; &lt;h4&gt;Iteration 4&lt;/h4&gt; &lt;p&gt;Iteration 4 was based on the previous iteration. All the methods that have been subclassed for testing should be moved to delegates and passed into the original class using a dependency injection approach.&lt;/p&gt; &lt;p&gt;I paired with a C++ guy who is doing embedded stuff during his day job on his C++ code. It turned out that we had quite different opinions and experiences. He was really focused on performance and couldn't understand why you would want to move methods to another class just to delegate to them as you are introducing overhead with the method call.&lt;/p&gt; &lt;p&gt;I haven't done any C++ programming since University. Eclipse seems to be suited well for development but compared to Java it still seems to lack a lot of convenience functionality. &lt;/p&gt; &lt;h4&gt;Iteration 5&lt;/h4&gt;&lt;br /&gt; &lt;p&gt;On Iteration 5 I paired with Tilman, a Clean Code Aficionado who I already knew from our &lt;a href=&quot;http://jug-karlsruhe.de/&quot;&gt;local Java User Group&lt;/a&gt;. We were supposed to change as many methods as possible to real functions that don't work on fields but on parameter values only.&lt;/p&gt; &lt;p&gt;A lot of people were struggling with this approach at first. But it turns out if you are doing this you have a really good starting position for doing further refactorings more easily. &lt;/p&gt; &lt;p&gt;My partner was doing most of the coding with some input from me. We were taking some directions I wouldn't have taken by myself but the resulting code was really well structured and could be reduced in size. Also we worked with an interesting Eclipse plugin I had seen before already: &lt;a href=&quot;http://infinitest.github.com/&quot;&gt;Infinitest&lt;/a&gt; always runs the tests in the background, no need to run the tests manually. Have to check if there's something like this available for Netbeans as well.&lt;/p&gt; &lt;h4&gt;Iteration 6&lt;/h4&gt; &lt;p&gt;To be honest, I don't know what the goal of the sixth iteration really was. I was pairing with a developer that was still fighting with the failing tests from the previous iteration. Most of the iteration we tried to get these running again. In the last few minutes we managed to extract some clases and clean up some code.&lt;/p&gt; &lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;The first german legacy code retreat really was a great experience. I learned a lot and, probably even more important, had a lot of fun.&lt;/p&gt;&lt;p&gt;The food and the location both were excellent. Thanks to the organizers &lt;a href=&quot;http://pboop.wordpress.com/&quot;&gt;Nicole and Andreas&lt;/a&gt; as well as the sponsors for making it possible. It's great to be able to attend a high quality event totally for free.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Running my Tests again</title>
   <link href="http://blog.florian-hopf.de/2012/01/running-my-tests-again.html"/>
   <updated>2012-01-11T05:29:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2012/01/running-my-tests-again</id>
   <content type="html">For some time I've been bugged by a &lt;a href=&quot;http://netbeans.org&quot;&gt;Netbeans&lt;/a&gt; problem that I couldn't find any solution to. When running a unit test from within Netbeans from time to time it happended that the tests just failed. They seemed to be executed in an old state. Running them again didn't help either, it seemed that some parts of the project didn't get recompiled. When executing the tests from a command line Maven build there were never any problems and afterwards the tests could be run again from Netbeans. The problem only occured very infrequently but nevertheless it was really annoying. I started not running the tests from Netbeans at all but only using Maven. That is also not a good solution as you either run all tests or have to edit the command line all the time for running only a single test.&lt;br /&gt;&lt;br /&gt;Recently I noticed what caused the problem: Netbeans has its &lt;a href=&quot;http://wiki.netbeans.org/FaqCompileOnSave&quot;&gt;compile on save&lt;/a&gt; feature on for tests. This means it is using its internal incremental compile feature which doesn't seem to work fine at least for some project setups. &lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-JL0unt9i9Zo/Twyz51UurNI/AAAAAAAAADA/Q5VIVX8HLxc/s1600/cos.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 86px;&quot; src=&quot;http://4.bp.blogspot.com/-JL0unt9i9Zo/Twyz51UurNI/AAAAAAAAADA/Q5VIVX8HLxc/s320/cos.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5696125434864774354&quot; /&gt;&lt;/a&gt;&lt;br /&gt;You can disable it in the project properties on the Build/Compile node. I haven't seen any problems since disabling it. Saves me a lot of time to run the tests from the IDE again.</content>
 </entry>
 
 <entry>
   <title>Talking about Code</title>
   <link href="http://blog.florian-hopf.de/2011/12/talking-about-code.html"/>
   <updated>2011-12-29T17:22:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/12/talking-about-code</id>
   <content type="html">Yesterday I attended the &lt;a href=&quot;http://groupspaces.com/softwerkskammer/&quot;&gt;Softwerkskammer&lt;/a&gt; Karlsruhe meetup for the first time. Softwerkskammer tries to connect the Software craftmanship community in Germany.&lt;br /&gt;&lt;br /&gt;The topic for the evening was simple: More Code. We looked at a lot of samples from a real project and discussed what's wrong with them and what could be done better. There were a lot of different opinions but that's a good thing as I got to question some habits I have when programming.&lt;br /&gt;&lt;br /&gt;This has been the first time I've been to a meeting where there is a lively discussion like this. The conferences and &lt;a href=&quot;http://jug-ka.de&quot;&gt;user groups&lt;/a&gt; I attend mostly have classic talks with one speaker and far less audience participation. Talking about code is a really good way to learn and this won't be the last time I attended a meetup. Thanks to the organizers.</content>
 </entry>
 
 <entry>
   <title>Spring in Action</title>
   <link href="http://blog.florian-hopf.de/2011/12/spring-in-action.html"/>
   <updated>2011-12-26T23:46:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/12/spring-in-action</id>
   <content type="html">&lt;p&gt;Sometimes it's comfortable to not be an absolute expert in a certain technology. This makes it really easy to learn new stuff, e.g. by profane methods like reading a book. Even if you are a Spring expert it is still likely that you will take something from the latest edition of &lt;a href=&quot;http://www.manning.com/walls4/&quot;&gt;Spring in Action by Craig Walls&lt;/a&gt; as it covers a wide range of topics. I haven't read one of the predecessors but people told me that those are even better.&lt;/p&gt;   &lt;p&gt;Having finished the book recently I just wanted to take the time to write down two interesting small configuration features that I learned from it.&lt;/p&gt; &lt;h4&gt;p-Namespace&lt;/h4&gt; &lt;p&gt;A feature that I just didn't know before but seems to be quite useful is the &lt;a href=&quot;http://static.springsource.org/spring/docs/3.0.6.RELEASE/spring-framework-reference/html/beans.html#beans-p-namespace&quot;&gt;p-Namespace&lt;/a&gt;. It's a namespace that is not backed by a schema and allows to configure beans in a really concise way. For example look at how a bean might be configured normally:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;    &amp;lt;bean id=&quot;foo&quot; class=&quot;foo.bar.Baz&quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&quot;myLongProperty&quot; value=&quot;2&quot;/&amp;gt;&lt;br /&gt;        &amp;lt;property name=&quot;myStringProperty&quot; value=&quot;Hallo&quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;The properties we'd like to set are children of the bean node. Netbeans comes with nice autocompletion support for the property names as you can see from the screenshot.&lt;p&gt; &lt;a href=&quot;http://2.bp.blogspot.com/-gFYvhLg2vwo/TvmGWCb6GRI/AAAAAAAAACw/AFwJLfc-5eU/s1600/blog-netbeans-spring-property.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 86px;&quot; src=&quot;http://2.bp.blogspot.com/-gFYvhLg2vwo/TvmGWCb6GRI/AAAAAAAAACw/AFwJLfc-5eU/s320/blog-netbeans-spring-property.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5690727317328501010&quot; /&gt;&lt;/a&gt; &lt;p&gt;The p-Namespace is a more concise version where the property names itself become attributes of the bean node:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;    &amp;lt;bean id=&quot;foo&quot; class=&quot;foo.bar.Baz&quot;&lt;br /&gt;        p:myLongProperty=&quot;2&quot; p:myStringProperty=&quot;Hallo&quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt;   &lt;p&gt;See that Netbeans is also clever enough to offer code completion here as well. &lt;/p&gt; &lt;a href=&quot;http://4.bp.blogspot.com/-cwq2dtJ9eEw/TvmGIRSzexI/AAAAAAAAACk/RcPWwIeLjY0/s1600/blog-netbeans-spring-pnamespace.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 116px;&quot; src=&quot;http://4.bp.blogspot.com/-cwq2dtJ9eEw/TvmGIRSzexI/AAAAAAAAACk/RcPWwIeLjY0/s320/blog-netbeans-spring-pnamespace.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5690727080798681874&quot; /&gt;&lt;/a&gt; &lt;p&gt;I am not sure if I will use the short form of the p-Namespace a lot. A consistent use of the features in a project is quite important so I think if the short form is used it should be used everywhere in the project.&lt;p&gt; &lt;h4&gt;Accessing Constants&lt;/h4&gt; &lt;p&gt;Sometimes you need to access some constants in your Spring configuration files. There are several ways to handle this, one of it using the util-Namespace:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;property name=&quot;day&quot;&amp;gt;&lt;br /&gt;    &amp;lt;util:constant static-field=&quot;java.util.Calendar.WEDNESDAY&quot;/&amp;gt;&lt;br /&gt;&amp;lt;/property&amp;gt;&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;Another way can be to use the Spring Expression Language to access it:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;property name=&quot;day&quot; value=&quot;#{T(java.util.Calendar).WEDNESDAY}&quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;I think this can be used more commonly as the value doesn't need to be registered as a subnode. For example I had some problems using util:constant as key or value in a util:map. That would have been easy just using the EL version.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Not another Diamond Operator Introduction</title>
   <link href="http://blog.florian-hopf.de/2011/12/not-another-diamond-operator.html"/>
   <updated>2011-12-08T05:46:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/12/not-another-diamond-operator</id>
   <content type="html">I just returned from the talk &quot;Lucky Seven&quot; of our local &lt;a href=&quot;http://jug-ka.de/&quot;&gt;Java User Group&lt;/a&gt;. It was far better than I expected. Not that I expected Wolfgang Weigend to be a bad speaker but though I organized the event I got the feeling that I had seen one too many Java 7 introductions already. But there was more ...&lt;br /&gt;&lt;br /&gt;One of the interesting aspects that I haven't been paying that much attention to is the merge of the &lt;a href=&quot;http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html&quot;&gt;JRockit&lt;/a&gt; and &lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/tech/index-jsp-136373.html&quot;&gt;Hotspot VM&lt;/a&gt;. Hotspot will be the base of the new development and JRockit features will be merged in. Some of these features will already be available in OpenJDK during the JDK 7 timespan.&lt;br /&gt;&lt;br /&gt;One of the changes got some amount of interest lately: &lt;a href=&quot;http://openjdk.java.net/jeps/122&quot;&gt;The PermGen space will be removed.&lt;/a&gt; Sounds like a major change but, once it works, it will definitively be a huge benefit.&lt;br /&gt;&lt;br /&gt;JRockit has been highly respected for its monitoring features. Among those is the interesting &lt;a href=&quot;http://docs.oracle.com/cd/E15289_01/doc.40/e15070/introduction.htm&quot;&gt;Java Flight Recorder&lt;/a&gt; that reminds me of the commercial project &lt;a href=&quot;http://www.chrononsystems.com/&quot;&gt;Chronon&lt;/a&gt;. It will be an always on recording  of data in the JVM that can be used for diagnostic purposes. Sounds really interesting!&lt;br /&gt;&lt;br /&gt;The overall goal of the convergence is to have a VM that can tune itself. Looking forward to it!&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://jug-karlsruhe.mixxt.de/networks/files/file.84658&quot;&gt;The (mixed german and english) slides of the talk are available for download.&lt;/a&gt;</content>
 </entry>
 
 <entry>
   <title>Getting started with Gradle</title>
   <link href="http://blog.florian-hopf.de/2011/10/getting-started-with-gradle.html"/>
   <updated>2011-10-26T16:58:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/10/getting-started-with-gradle</id>
   <content type="html">&lt;a href=&quot;http://maven.apache.org/&quot;&gt;Maven&lt;/a&gt; has been my build tool of choice for some years now. Coming from Ant the declarative approach, useful conventions as well as the dependency management offered a huge benefit. But as with most technologies the more you are using it the more minor and major flaws appear. A big problem is that with Maven builds are sometimes not reproducable. The outcome of the build is influenced by the state of your local repository.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://gradle.org/&quot;&gt;Gradle&lt;/a&gt; is a &lt;a href=&quot;http://groovy.codehaus.org/&quot;&gt;Groovy&lt;/a&gt; based build system that is often recommended as a more advanced system. The features that make it appealing to me are probably the easier syntax and the &lt;a href=&quot;http://forums.gradle.org/gradle/topics/welcome_to_our_new_dependency_cache&quot;&gt;advanced dependency cache&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For a &lt;a href=&quot;https://github.com/fhopf/opencms-rfs-driver&quot;&gt;recent project that I just uploaded for someone else&lt;/a&gt; I needed to add a simple way to build the jar. Time to do it with Gradle and see what it feels like.&lt;br /&gt;&lt;h4&gt;The build script&lt;/h4&gt;&lt;br /&gt;The purpose of the build is simple: compile some classes with some dependencies and package those to a jar file. Same as Maven and Ant, Gradle also needs at least one file that describes the build. This is what build.gradle looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;apply plugin: 'java'&lt;br /&gt;&lt;br /&gt;repositories {&lt;br /&gt;    mavenCentral()&lt;br /&gt;    mavenRepo url: &quot;http://bp-cms-commons.sourceforge.net/m2repo&quot;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;dependencies {&lt;br /&gt;    compile group: 'org.opencms', name: 'opencms-core', version: '7.5.4'&lt;br /&gt;    compile group: 'javax.servlet', name: 'servlet-api', version: '2.5'&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sourceSets {&lt;br /&gt;    main {&lt;br /&gt;        java {&lt;br /&gt;            srcDir 'src'&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's step through the file line by line. The first line tells gradle to use the java plugin. This plugin ships with tasks for compiling and packaging java classes.&lt;br /&gt;&lt;br /&gt;In the next block we are declaring some dependency repositories. Luckily Gradle supports Maven repositories so existing repositories like Maven central can be used. I guess without this feature Gradle would not gain a lot of adoption at all. There are two repos declared: Maven central where most of the common dependencies are stored and a custom repo that provides the OpenCms dependencies.&lt;br /&gt;&lt;br /&gt;The next block is used to declare which dependencies are necessary for the build. Gradle also supports scopes (in Gradle: configurations) so for example you can declare that some jars are only needed during test run. The dependency declaration is in this case similar to the Maven coordinates but Gradle also supports more advanced features like &lt;a href=&quot;http://gradle.org/current/docs/userguide/artifact_dependencies_tutorial.html&quot;&gt;version ranges&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The last block isn't really necessary. It's only there because my Java sources are located in src instead of the default src/main/java. Gradle uses a lot of the Maven conventions so it's really easy to migrate builds.&lt;br /&gt;&lt;h4&gt;Building&lt;/h4&gt;&lt;br /&gt;To build the project you need Gradle installed. You can download a single distribution that already packages Grooovy and all the needed files. You only need to add the bin folder to your path.&lt;br /&gt;&lt;br /&gt;Packaging the jar is easy: You just run the jar task in the java plugin: gradle :jar. Gradle will start to download all direct and transitive dependencies. The fun part: It uses a nice command line library that can display text in bold, rewrite lines and the like. Fun to watch it.&lt;br /&gt;&lt;br /&gt;I like the simplicity and readability of the build script. You don't need to declare anything if you don't really need it. No coordinates, no schema declaration, nothing. I hope I will find time to use it in a larger project so I can see what it really feels like in the daily project work.</content>
 </entry>
 
 <entry>
   <title>Book Review: Solr 1.4 Enterprise Search Server</title>
   <link href="http://blog.florian-hopf.de/2011/03/book-review-solr-14-enterprise-search.html"/>
   <updated>2011-03-01T03:08:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/03/book-review-solr-14-enterprise-search</id>
   <content type="html">I've been interested in &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Solr&lt;/a&gt; since I read about it the first time, must have been some time in 2008, doing some research for a search centric web page that was supposed to be run on OpenCms but unfortunately was never developed. At that time I wouldn't have used it as I hadn't heard about it before but I liked the idea a lot. After having attended the &lt;a href=&quot;http://parleys.com/#sl=1&amp;st=5&amp;id=1546&quot;&gt;Devoxx university session by Eric Hatcher&lt;/a&gt; on Solr in 2009 I was completely sure that the next search system I would implement would be based on Solr. The project's nearly finished now, time to recap what I took out of the book I got for learning Solr.&lt;br /&gt;&lt;br /&gt;First of all, when learning a new technology I prefer paper books over internet research. Though there are other books available,&lt;a href=&quot;https://www.packtpub.com/solr-1-4-enterprise-search-server/book&quot;&gt; Solr 1.4 Enterprise Search Server by David Smiley and Eric Pugh&lt;/a&gt; seems to be the one that is most often recommended.&lt;br /&gt;&lt;br /&gt;The book starts off with a high level introduction into what Solr and Lucene are, some first examples and interestingly, how to build Solr from source. Though the book was released before Solr 1.4 the authors seemed to have the foresight that some features might still be lacking and had to be included manually. In fact, I've never seen an open source project where applying patches is such a common thing as it seems to be the case for Solr.&lt;br /&gt;&lt;br /&gt;Schema configuration and text analysis are the topics for the second chapter. It begins with an introduction into &lt;a href=&quot;http://musicbrainz.org/&quot;&gt;MusicBrainz&lt;/a&gt;, a freely available data set of music data is used as an example throughout the book. This chapter is crucial to the understanding of Solr as it introduces a lot of Lucene concepts that probably not every reader is familiar with.&lt;br /&gt;&lt;br /&gt;After quite some theory chapter 3 starts with the practical parts, covering the indexing process. Curl, the command line http client, is used to send data to solr and retrieve it. Another option, the data import handler, that directly imports data from a database, is also introduced.&lt;br /&gt;&lt;br /&gt;Chapter 4 to 6 walk the reader through the search process and several useful components to enhance the users search experience like faceting and the dismax request handler. This is the part where Solr really shines as you can see how easy it is to integrate new features in your application that probably would have taken a long time to develop using plain Lucene.&lt;br /&gt;&lt;br /&gt;Deploying Solr is covered in Chapter 7 with quite some useful information on configuring and monitoring a Solr instance. Chapter 8 looks at some client APIs from different programmin languages, SolrJ being the most important to me. The book ends with an in-depth look at how Solr can be tunded and scaled.&lt;br /&gt;&lt;br /&gt;I can say that this is a really excellent book, as an introduction to Solr as well as a reference while developing your application. The most common use cases are covered, the examples make it really easy to adopt the concepts in your application. There are lots of hands on information that prove useful during development and deployment of your application.&lt;br /&gt;&lt;br /&gt;Some slight drawbacks I don't want to keep to myself: As the common message format for Solr is a custom XML dialect, there is a lot of XML in the book to digest. As it's so common to use it that's not necessarily a bad thing but you might get quite dizzy looking at a lot of angle brackets. From a readers perspective some variety would have been nice e.g. by mixing XML with the Ruby format or JSON or introducing client APIs earlier. Also, while it's a good idea to use a data set that is freely available, MusicBrainz probably isn't the best format for demoing some features. There are no large text sections or documents, which are often what a search application will be build on. And finally, not really an issue of the authors but rather of the publisher, PacktPub: When skimming through the book it's quite hard to see when a new section begins. The headlines do not contain a numbering scheme and are of a very similar size.&lt;br /&gt;&lt;br /&gt;Nevertheless, if you have to develop an application using Solr, you should by all means buy this book, you won't regret it.</content>
 </entry>
 
 <entry>
   <title>Running Ruby on Rails Tests in Netbeans</title>
   <link href="http://blog.florian-hopf.de/2011/01/running-ruby-on-rails-tests-in-netbeans.html"/>
   <updated>2011-01-23T09:55:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/01/running-ruby-on-rails-tests-in-netbeans</id>
   <content type="html">I don't get it. Netbeans is often recommended as an excellent IDE for Ruby on Rails development, not only when targeting the JVM. Nevertheless, even some basic features don't seem to be working with the default setup. You can't even run the tests, which is fundamental to developing using a dynamic language.&lt;br /&gt;&lt;br /&gt;What's happening? Suppose you have a simple app and you want to run some tests using the test database. Not sure if this is mandatory when using the built in JRuby but it &lt;a href=&quot;http://netbeans.org/kb/docs/ruby/rapid-ruby-weblog.html&quot;&gt;seems to be normal&lt;/a&gt; to use the jdbcmysql adapter. When you try to run the tests you will see something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;1) Error:&lt;br /&gt;test_index_is_ok(ContactsControllerTest):&lt;br /&gt;ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: Table 'kontakt_test.contacts' doesn't exist: DELETE FROM `contacts`&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;followed by the stack trace that isn't really helpful as it's not the root cause. Rails somehow doesn't create the tables in the test database. You'll see a more helpful output when starting the rake task &quot;db:test:prepare&quot; directly in debug mode:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;** Invoke db:test:prepare (first_time)&lt;br /&gt;** Invoke db:abort_if_pending_migrations (first_time)&lt;br /&gt;** Invoke environment (first_time)&lt;br /&gt;** Execute environment&lt;br /&gt;** Execute db:abort_if_pending_migrations&lt;br /&gt;rake aborted!&lt;br /&gt;Task not supported by 'jdbcmysql'&lt;br /&gt;/path/to/netbeans-6.9.1/ruby/jruby-1.5.1/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/tasks/databases.rake:380&lt;br /&gt;/path/to/netbeans/ruby/jruby-1.5.1/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `call'&lt;br /&gt;/path/to/netbeans/ruby/jruby-1.5.1/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `execute'&lt;br /&gt;/path/to/netbeans/ruby/jruby-1.5.1/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `each'&lt;br /&gt;[...]&lt;br /&gt;** Execute db:test:prepare&lt;br /&gt;** Invoke db:test:load (first_time)&lt;br /&gt;** Invoke db:test:purge (first_time)&lt;br /&gt;** Invoke environment &lt;br /&gt;** Execute db:test:purge&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The task fails in the database task in the rails lib. You can open up the source code by opening the node &lt;code&gt;Libraries/Built-in-JRuby/rails-2.3.8/lib/tasks/databases.rake&lt;/code&gt; in Netbeans.&lt;br /&gt;&lt;br /&gt;At line 357 you can see the problem: Rails only expexts some hardcoded adapters, jdbcmysql not being one of them. It skips the task for unknown adapters. Two options to fix it: Insert a regular expression that matches both:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;when /mysql/ # instead of when &quot;mysql&quot;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or add the jdbcmysql adapter as a second option:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;when &quot;mysql&quot;,&quot;jdbcmysql&quot;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;Now the tests are running and hopefully passing. The same kind of error might occur for other tasks as well as there are some more checks for the mysql adapter in this file. You should be able to fix them the same way.&lt;br /&gt;&lt;br /&gt;I wouldn't have expected to have to patch the rails code for using it in Netbeans but this doesn't seem to be &lt;a href=&quot;http://markmail.org/thread/w6iwdrtwq32oktir#query:+page:1+mid:wjysi5gab76dtdas+state:results&quot;&gt;uncommon&lt;/a&gt;. &lt;a href=&quot;http://blog.nicksieger.com/articles/2009/10/12/fresh-0-9-2-activerecord-jdbc-adapter-release&quot;&gt;Using a recent active record version&lt;/a&gt; is supposed to fix the problem as you can use mysql as an adapter name then but I didn't find a way to run the jdbc generator from Netbeans. It isn't available in the list of generators and I didn't find a generator gem to download.&lt;br /&gt;&lt;br /&gt;What's to be learned for me from this? I got a better understanding of how the build process  works using rake. But more importantly: even technologies that have been hyped for a long time might not be that flawless as you would expect.</content>
 </entry>
 
 <entry>
   <title>Refactoring in Git</title>
   <link href="http://blog.florian-hopf.de/2011/01/refactoring-in-git.html"/>
   <updated>2011-01-09T20:49:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/01/refactoring-in-git</id>
   <content type="html">&lt;p&gt;To me, when using SVN, the most important reason for using an IDE plugin was the refactoring support: SVN doesn't notice when you rename a file, you have to explicitly call svn mv.&lt;/p&gt; &lt;p&gt;I thought this would be a major problem with Git, as a Java refactoring changes the content and the filename in one go. As the content changes the SHA1-checksum also changes and you'd run into problems. Fortunately, that's not the case.&lt;/p&gt; &lt;p&gt;With Git, you don't need a special operation: It detects renames with minor changes automatically.&lt;/p&gt; &lt;p&gt;Time for a test. Suppose you have a simple Java class like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public class TestClass {&lt;br /&gt;&lt;br /&gt;    public static void main(String [] args) {&lt;br /&gt;        System.out.println(&quot;Hello Git&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Commit it to the Git repository: &lt;/p&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-netbeans$ git add src/TestClass.java&lt;br /&gt;flo@hank:~/git-netbeans$ git commit -m &quot;added test class&quot;&lt;br /&gt;[master 9269c2f] added test class&lt;br /&gt; 1 files changed, 7 insertions(+), 0 deletions(-)&lt;br /&gt; create mode 100644 src/TestClass.java&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Rename the class (either by using an IDE or by executing a manual refactoring by changing the file name and the class name):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;public class TestClassWithNewName {&lt;br /&gt;&lt;br /&gt;    public static void main(String [] args) {&lt;br /&gt;        System.out.println(&quot;Hello Git&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;&lt;code&gt;git status&lt;/code&gt; will tell you something like this:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-netbeans$ git status&lt;br /&gt;# On branch master&lt;br /&gt;# Changed but not updated:&lt;br /&gt;#   (use &quot;git add/rm &lt;file&gt;...&quot; to update what will be committed)&lt;br /&gt;#   (use &quot;git checkout -- &lt;file&gt;...&quot; to discard changes in working directory)&lt;br /&gt;#&lt;br /&gt;#       deleted:    src/TestClass.java&lt;br /&gt;#&lt;br /&gt;# Untracked files:&lt;br /&gt;#   (use &quot;git add &lt;file&gt;...&quot; to include in what will be committed)&lt;br /&gt;#&lt;br /&gt;#       src/TestClassWithNewName.java&lt;br /&gt;no changes added to commit (use &quot;git add&quot; and/or &quot;git commit -a&quot;)&lt;/pre&gt;&lt;/code&gt; &lt;p&gt;Doesn't look that good yet. It detects an added and a removed file. Next, stage the changes and have another look at the status:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-netbeans$ git rm src/TestClass.java&lt;br /&gt;rm 'src/TestClass.java'&lt;br /&gt;flo@hank:~/git-netbeans$ git add src/TestClassWithNewName.java&lt;br /&gt;flo@hank:~/git-netbeans$ git status&lt;br /&gt;# On branch master&lt;br /&gt;# Changes to be committed:&lt;br /&gt;#   (use &quot;git reset HEAD &lt;file&gt;...&quot; to unstage)&lt;br /&gt;#&lt;br /&gt;#       renamed:    src/TestClass.java -&gt; src/TestClassWithNewName.java&lt;br /&gt;#&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Neat, Git detected a rename. Let's commit and see the log:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-netbeans$ git commit -m &quot;refactored class&quot;&lt;br /&gt;[master 4acd7f1] refactored class&lt;br /&gt; 1 files changed, 1 insertions(+), 1 deletions(-)&lt;br /&gt; rename src/{TestClass.java =&gt; TestClassWithNewName.java} (72%)&lt;br /&gt;flo@hank:~/git-netbeans$ git log src/TestClassWithNewName.java&lt;br /&gt;commit 4acd7f19ccd6cc02816ee7f1293ea5a69d7a4ca7&lt;br /&gt;Author: Florian Hopf &lt;fhopf@web.de&gt;&lt;br /&gt;Date:   Sun Jan 9 14:27:59 2011 +0100&lt;br /&gt;&lt;br /&gt;    refactored class&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Hmmm, only the last commit? Looks like we have to tell that we want to follow renames:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-netbeans$ git log --follow src/TestClassWithNewName.java&lt;br /&gt;commit 4acd7f19ccd6cc02816ee7f1293ea5a69d7a4ca7&lt;br /&gt;Author: Florian Hopf &lt;fhopf@web.de&gt;&lt;br /&gt;Date:   Sun Jan 9 14:27:59 2011 +0100&lt;br /&gt;&lt;br /&gt;    refactored class&lt;br /&gt;&lt;br /&gt;commit 9269c2fd194b2bd2b93a18ab88f21fb2180c5870&lt;br /&gt;Author: Florian Hopf &lt;fhopf@web.de&gt;&lt;br /&gt;Date:   Sun Jan 9 13:48:35 2011 +0100&lt;br /&gt;&lt;br /&gt;    added test class&lt;/code&gt; &lt;/pre&gt; &lt;p&gt;What do I take from this experiment? I guess I won't use the &lt;a href=&quot;http://nbgit.org/&quot;&gt;Netbeans Git plugin&lt;/a&gt; for now. I still have to get acquainted to the command line and its better to learn the basics first.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Git hook for Redmine messages</title>
   <link href="http://blog.florian-hopf.de/2011/01/git-hook-for-redmine-messages.html"/>
   <updated>2011-01-09T18:40:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/01/git-hook-for-redmine-messages</id>
   <content type="html">&lt;p&gt;At work we are using &lt;a href=&quot;http://www.redmine.org/&quot;&gt;Redmine&lt;/a&gt; with the &lt;a href=&quot;http://www.redmine.org/projects/redmine/wiki/redminesettings#Referencing-issues-in-commit-messages&quot;&gt;repository references&lt;/a&gt; enabled. When adding special terms like refs #1234 or fixes #1234 to the commit message the commit is automatically assigned to ticket 1234 and shown with the ticket. Only commiting code that references a ticket is considered to be a best practice as all changes are documented with a ticket.&lt;/p&gt; &lt;p&gt;As I'm using the Git SVN bridge now I tend to commit more than using plain SVN. Often I just forget to add the refs marker which is quite annoying. &lt;a href=&quot;http://progit.org/book/ch7-4.html&quot;&gt;Pro Git&lt;/a&gt; introduces a hook that can be used to check your commit message for a special format.&lt;/p&gt; &lt;p&gt;This is the shamelessly copied hook, adjusted to the Redmine keywords:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;#!/usr/bin/env ruby&lt;br /&gt;message_file = ARGV[0]&lt;br /&gt;message = File.read(message_file)&lt;br /&gt;&lt;br /&gt;$regex = /(refs #(\d+)|fixes #(\d+))/&lt;br /&gt;&lt;br /&gt;if !$regex.match(message)&lt;br /&gt;  puts &quot;Your message is not formatted correctly (missing refs #XXX or fixes #XXX)&quot;&lt;br /&gt;  exit 1&lt;br /&gt;end&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;How to use it? Copy the code to the file &lt;code&gt;.git/hooks/commit-msg&lt;/code&gt; in your project and make it executable (&lt;code&gt;chmod +x .git/hooks/commit-msg&lt;/code&gt;).&lt;/p&gt; &lt;p&gt;Try to commit without the markers:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-redmine$ git commit -am &quot;commit that doesn't reference a ticket&quot;&lt;br /&gt;Your message is not formatted correctly (missing refs #XXX or fixes #XXX)&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;And with a marker:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-redmine$ git commit -am &quot;commit that references a ticket, refs #1234&quot;&lt;br /&gt;[master 189b6b1] commit that references a ticket, refs #1234&lt;br /&gt; 1 files changed, 2 insertions(+), 0 deletions(-)&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;If you want to skip the hook for some reason you can do so using the &lt;code&gt;--no-verify&lt;/code&gt; option:&lt;/p/&gt; &lt;pre&gt;&lt;code&gt;flo@hank:~/git-redmine$ git commit --no-verify -am &quot;special commit that doesn't reference a ticket&quot;&lt;br /&gt;[master d1c0698] special commit that doesn't reference a ticket&lt;br /&gt; 1 files changed, 1 insertions(+), 0 deletions(-)&lt;/code&gt;&lt;/pre&gt;</content>
 </entry>
 
 <entry>
   <title>GoGear title management in Sqllite</title>
   <link href="http://blog.florian-hopf.de/2011/01/gogear-title-management-in-sqllite.html"/>
   <updated>2011-01-06T18:20:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2011/01/gogear-title-management-in-sqllite</id>
   <content type="html">&lt;i&gt;While writing this post I noticed that I am just wrong with my assumptions on my GoGear device. Read on to learn why. I'll publish it anyway as the information should still be valid for older versions of GoGear.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I've been looking for a way to enable the bookmarks feature for audio books for my &lt;a href=&quot;http://www.p4c.philips.com/cgi-bin/dcbint/cpindex.pl?ctn=SA1922/02&quot;&gt;Philips GoGear SA1922&lt;/a&gt;. Unfortunately according to &lt;a href=&quot;http://download.p4c.philips.com/sca/sca/090715/090715120513_28515.JPG&quot;&gt;this image&lt;/a&gt;, which is only displayed on the german site, it's not supported for this version though with the latest firmware there's a special menu option for audio books.&lt;br /&gt;&lt;br /&gt;While experimenting I learned a few things along the way that are quite interesting. GoGear uses &lt;a href=&quot;http://www.sqlite.org/&quot;&gt;Sqlite&lt;/a&gt; for managing all meta information of the audio files stored. I use &lt;a href=&quot;http://golb.sourceforge.net/&quot;&gt;golb&lt;/a&gt; to transfer music from my Linux machine which does all the magic of extracting ID3 tags and inserting all data in the database. Normally you would use &lt;code&gt;golb -f _system/media/audio/MyDb&lt;/code&gt; in the root folder of the mounted storage device to scan all files on the device and write it to the database _system/media/audio/MyDb.&lt;br /&gt;&lt;br /&gt;If you want to see or manipulate the data you can use the sqlite client: In the same folder call &lt;code&gt;sqlite _system/media/audio/MyDb&lt;/code&gt;. This will open a client console similar to mysql:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;flo@hank:/media/PHILIPS$ sqlite _system/media/audio/MyDb&lt;br /&gt;SQLite version 2.8.17&lt;br /&gt;Enter &quot;.help&quot; for instructions&lt;br /&gt;sqlite&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;To see the schema information you can issue the .schema command, which display information on all the tables and its indexes:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;sqlite&gt; .schema&lt;br /&gt;CREATE TABLE albumTable(         iAlbumId               INTEGER PRIMARY KEY, cAlbumTitle        VARCHAR(100) );&lt;br /&gt;CREATE TABLE artistTable(        iArtistId              INTEGER PRIMARY KEY, cArtistName        VARCHAR(100) );&lt;br /&gt;CREATE TABLE dirTable(           iDirId                 INTEGER PRIMARY KEY, cDirName           VARCHAR(260),iParentDirId       INTEGER );&lt;br /&gt;CREATE TABLE genreTable(         iGenreId               INTEGER PRIMARY KEY, cGenreName         VARCHAR(50) );&lt;br /&gt;CREATE TABLE playlistTable( iPlaylistId INTEGER PRIMARY KEY,cPlaylistName       VARCHAR(100), cFileName         VARCHAR(260),iDirId                     INTEGER );&lt;br /&gt;CREATE TABLE playsongTable( iPlaysongId INTEGER PRIMARY KEY,iPlaylistId INTEGER, iOrderNr       INTEGER,iSongId         INTEGER );&lt;br /&gt;CREATE TABLE songTable (         iSongId                INTEGER PRIMARY KEY,cSongTitle  VARCHAR(100),iArtistId          INTEGER,iAlbumId                INTEGER,iTrackNr            INT8,iTrackLength       INT16,iNrPlayed         INT16,cFileName         VARCHAR(260),iDirId                     INTEGER,iYear      INT8,iGenreId            INTEGER,iBitRate       INTEGER,iSampleRate    INTEGER,iFileSize      INTEGER,iMediaType     INTEGER );&lt;br /&gt;CREATE INDEX album_cAlbumTitle ON albumTable (cAlbumTitle);&lt;br /&gt;CREATE INDEX artist_cArtistName ON artistTable (cArtistName);&lt;br /&gt;CREATE INDEX dir_cDirName ON dirTable (cDirName);&lt;br /&gt;CREATE INDEX dir_iParentDirId ON dirTable (iParentDirId);&lt;br /&gt;CREATE INDEX genre_cGenreName ON genreTable (cGenreName);&lt;br /&gt;CREATE INDEX playlist_cPlaylistName ON playlistTable (cPlaylistName);&lt;br /&gt;CREATE INDEX playsong_iOrderNr ON playsongTable (iOrderNr);&lt;br /&gt;CREATE INDEX playsong_iPlaylistId ON playsongTable (iPlaylistId);&lt;br /&gt;CREATE INDEX playsong_iSongId ON playsongTable (iSongId);&lt;br /&gt;CREATE INDEX song_cFileName ON songTable (cFileName);&lt;br /&gt;CREATE INDEX song_cSongTitle ON songTable (cSongTitle);&lt;br /&gt;CREATE INDEX song_iAlbumId ON songTable (iAlbumId);&lt;br /&gt;CREATE INDEX song_iArtistId ON songTable (iArtistId);&lt;br /&gt;CREATE INDEX song_iDirId ON songTable (iDirId);&lt;br /&gt;CREATE INDEX song_iGenre ON songTable (iGenreId);&lt;br /&gt;CREATE INDEX song_iTrackNr ON songTable (iTrackNr);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;To see some of the song information you can query the songTable:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;sqlite&gt; select * from songTable limit 3;&lt;br /&gt;1|CRE041 Sprachen|1|1|0|4476|0|chaosradio_express_041.mp3|28|2007|1|128|44100|71709702|1&lt;br /&gt;2|Java Posse #331 - Roundup '10 - Modules|2|2|331|3783|0|JavaPosse331.mp3|28|2010|2|96|44100|45460832|1&lt;br /&gt;3|CRE080 Geschichte der Typographie|1|1|0|7947|0|chaosradio_express_080.mp3|28|2008|3|128|44100|127247455|1&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;You can use standard sql to update the information:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;sqlite&gt; update songTable set cSongTitle = &quot;Sprachen - Chaos Radio Express 41&quot; where iSongId = 1;&lt;br /&gt;&lt;/code&gt; &lt;br /&gt;&lt;i&gt;At this point I was about to hit the publish button for the post. Luckily I tried if the update happened at all. Turned on my device: Still the old title. Rebootet the device, deleted and recreated all indexes, inspected the Golb sourcecode, found nothing. After a while it struck me: I don't need golb for my GoGear version. This device seems to extract all information from the id3 tags directly, very likely during startup. Classic fail!&lt;/i&gt;</content>
 </entry>
 
 <entry>
   <title>Podcasts for developers</title>
   <link href="http://blog.florian-hopf.de/2010/12/podcasts-for-developers.html"/>
   <updated>2010-12-31T17:28:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2010/12/podcasts-for-developers</id>
   <content type="html">As I'm walking to the office every day I've incorporated the habit of listening to podcasts on my way. I'd like to list some of them which might be of interest to other developers.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://javaposse.com/&quot;&gt;The Javaposse&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;An almost legendary Java podcast. Interesting development news are discussed in short and sometimes in length.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://hanselminutes.com/&quot;&gt;Hanselminutes&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;Scott Hanselman is a .Net developer who also likes to see beyond his own nose. There are a lot of topics he discusses that are also relevant and interesting if you're not into Microsoft.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://twit.tv/floss&quot;&gt;FLOSS Weekly&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;A sometimes weekly show where different people from the open source universe are interviewed. I only listen to some episodes that attract my attention but it's always enoyable.   &lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://linuxoutlaws.com/&quot;&gt;Linux-Outlaws&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;The two hosts rant about everything Linux and open source. It's always a lot of fun but the episodes tend to get quite long for a weekly show.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://se-radio.net/&quot;&gt;SE-Radio&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;Mainly an interview podcast on everything software development. A lot of high class guests have been featured so far.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://webdevradio.com/&quot;&gt;WebDevRadio&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;Covers web development topics using different server side technologies, from ASP to Grails.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://improvingpodcasts.com/&quot;&gt;Improving Podcasts&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;A company podcast with some development episodes and a lot of agile topics. I get the feeling that it's slowly dying as only few new episodes are produced.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://blog.stackoverflow.com/category/podcasts/&quot;&gt;Stackoverflow&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;The discontinued podcast of Jeff Atwood and Joel Spolsky. A lot of episodes are still worth listening to.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;German Podcasts&lt;/h2&gt;&lt;br /&gt;Es gibt natürlich auch interessante deutsche Podcasts.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://www.heise.de/developer/podcast/&quot;&gt;Software-ArchitektTOUR&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;Manche Episoden sind ziemlich interessant und unterhaltsam, ich habe mich jedoch auch schon über manche Inhalte geärgert. Hängt glaube ich ein bisschen an den jeweiligen Hosts, ob es ein für mich interessantes Thema wird oder nicht.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href=&quot;http://chaosradio.ccc.de/chaosradio_express.html&quot;&gt;Chaosradio Express&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;Der Interview-Podcast des Chaos Computer Clubs. Es dreht sich nicht alles um IT, wer beispielsweise wissen will, wie das Universum aufgebaut ist oder einen Einblick in das Leben in der ehemaligen DDR haben will ist hier auch genau richtig.</content>
 </entry>
 
 <entry>
   <title>Slides and sample code for Solr talk</title>
   <link href="http://blog.florian-hopf.de/2010/11/slides-and-sample-code-for-solr-talk.html"/>
   <updated>2010-11-21T03:03:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2010/11/slides-and-sample-code-for-solr-talk</id>
   <content type="html">I just uploaded the slides and sample code for my talk about &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Apache Solr&lt;/a&gt; at the &lt;a href=&quot;http://jug-karlsruhe.mixxt.de/networks/events/show_event.25567&quot;&gt;Java User Group Karlsruhe&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The sample consists of an example solr configuration and some scripts that can be used to index files in Solr. A simple Spring MVC app can be used to search the content. Go to &lt;a href=&quot;https://github.com/fhopf/solr-sample-jugka&quot;&gt;Github&lt;/a&gt; and grab the application as well as the &lt;a href=&quot;https://github.com/fhopf/solr-sample-jugka/raw/master/solr.pdf&quot;&gt;slides&lt;/a&gt; if you're interested.</content>
 </entry>
 
 <entry>
   <title>Don't try to do it all</title>
   <link href="http://blog.florian-hopf.de/2010/11/dont-try-to-do-it-all.html"/>
   <updated>2010-11-07T14:22:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2010/11/dont-try-to-do-it-all</id>
   <content type="html">For several years I have been running a web site for &lt;a href=&quot;http://hans-hopf.de/&quot;&gt;my father&lt;/a&gt; which contains some information about his work. It started out from html that was served statically, in the beginning of 2007 I migrated the content to &lt;a href=&quot;http://opencms.org&quot;&gt;OpenCms&lt;/a&gt;. During this time I was very eager to learn everything that was related to building a web site from scratch, from the internal workings of OpenCms to Apache configuration and CSS and its peculiarities.&lt;br /&gt;&lt;br /&gt;Not that any of these are areas that are not important to know as somebody who is doing web development professionally. But for spare-time projects you have to be careful that you do not run out of time, which is exactly what happened to me. Especially for the styling of the page I did not invest as much time as I would have needed. I started from plain html and did all of styling my myself. I read a lot about the box model and how to apply CSS correctly. Unfortunately only the basic structure was finished before I ran out of time so I published the page with nearly no styling for the content. And that's how it stayed for 3 1/2 years ...&lt;br /&gt;&lt;br /&gt;Recently I invested some time in it again. I ported the layout to use the &lt;a href=&quot;http://www.yaml.de/&quot;&gt;YAML framework&lt;/a&gt; with its standard design and reorganized the structure of the content. Also, while at it, I updated from OpenCms 7.0.2 to OpenCms 7.5.2. In nearly no time the site was in a much better state than before without having to fight several browsers.&lt;br /&gt;&lt;br /&gt;The main things I should have done from the beginning:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Use a CSS framework or a ready made template unless you're willing to invest a lot of time&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Take extra care when structuring the content in OpenCms. Do not nest too deep if it's not necessary.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use a source code versioning system even if you're not planning to have a huge project&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Things that worked well during the relaunch:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://groovy.codehaus.org/Processing+XML&quot;&gt;Groovy&lt;/a&gt; is a nice tool to migrate content from one schema type to the other&lt;/li&gt;&lt;br /&gt;&lt;li&gt;YAML and its &lt;a href=&quot;http://builder.yaml.de/&quot;&gt;YAML-Builder&lt;/a&gt; are really easy to use&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Apache is configured in no time using &lt;a href=&quot;http://www.sebastian.himberger.de/blog/2009/03/11/opencms-apache-integration-the-simplest-solution/&quot;&gt;Sebastians excellent tutorial&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;There's still a lot of work to do: Some pages still use tables for layouting which especially doesn't work well anymore when using yaml as it highlights cells when hovering. Also, I'm planning to add a contact form using Ruby on Rails. Of course this could be easily done using &lt;a href=&quot;http://www.alkacon.com/en/modules/downloads/alkacon_oamp_webform_1.2.0.html&quot;&gt;Alkacon Webform&lt;/a&gt; but that's a good opportunity to learn a new technology.&lt;br /&gt;&lt;br /&gt;At least SEO-wise the site seems to be OK: We are &lt;a href=&quot;http://www.google.de/search?q=hans+hopf&quot;&gt;beating&lt;/a&gt; the famous &lt;a href=&quot;http://de.wikipedia.org/wiki/Hans_Hopf&quot;&gt;singer&lt;/a&gt; as well as the &lt;a href=&quot;http://www.hopfweisse.de/&quot;&gt;bavarian wheat beer company&lt;/a&gt; with the same name :)</content>
 </entry>
 
 <entry>
   <title>Using OpenCmsTestCase</title>
   <link href="http://blog.florian-hopf.de/2010/04/using-opencmstestcase.html"/>
   <updated>2010-04-14T06:32:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2010/04/using-opencmstestcase</id>
   <content type="html">These are basically some notes for me because I just had to relearn all of it.&lt;br /&gt;&lt;br /&gt;To use OpenCmsTestCase in a project the following steps have to be applied:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Download the source distribution and unzip it&lt;/li&gt;&lt;br /&gt;&lt;li&gt;set the file encoding: &lt;i&gt;export ANT_OPTS=-Dfile.encoding=iso-8859-1&lt;/i&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;run &lt;i&gt;ant bindist&lt;/i&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;run &lt;i&gt;ant compile-tests&lt;/i&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Somehow some unittests are always not compiling for me: remove the java files and the entries from the TestSuites&lt;/li&gt;&lt;br /&gt;&lt;li&gt;create a jar file from the folder &lt;i&gt;org&lt;/i&gt; in &lt;i&gt;../BuildCms/build/test&lt;/i&gt;, e.g. &lt;i&gt;jar -cf opencms-test-7.5.2.jar org&lt;/i&gt;&lt;/li&gt; &lt;br /&gt;&lt;li&gt;add the jar to your project classpath/deploy to your maven repository&lt;/li&gt;&lt;br /&gt;&lt;li&gt;add hsqldb.jar to your project&lt;/li&gt;&lt;br /&gt;&lt;li&gt;copy the folders &lt;i&gt;data&lt;/i&gt; and &lt;i&gt;webapp&lt;/i&gt; to your project&lt;/li&gt;&lt;br /&gt;&lt;li&gt;copy &lt;i&gt;test/log4j.properties&lt;/i&gt; and &lt;i&gt;test/test.properties&lt;/i&gt; to your test classpath and adjust the directory paths in &lt;i&gt;test.properties&lt;/i&gt; (a good reason to use Maven so you can use the resource filtering mechanism)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;play with the files in &lt;i&gt;data/imports&lt;/i&gt; and adjust them to your needs&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;A simple test case example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import org.opencms.file.CmsObject;&lt;br /&gt;import org.opencms.file.CmsResource;&lt;br /&gt;import org.opencms.test.OpenCmsTestCase;&lt;br /&gt;import org.opencms.test.OpenCmsTestProperties;&lt;br /&gt;&lt;br /&gt;public class DummyOpenCmsTest extends OpenCmsTestCase {&lt;br /&gt;&lt;br /&gt;    static {&lt;br /&gt;        OpenCmsTestProperties.initialize(OpenCmsTestProperties.getResourcePathFromClassloader(&quot;test.properties&quot;));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public DummyOpenCmsTest(String name) {&lt;br /&gt;        super(name);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void setUp() throws Exception {&lt;br /&gt;        super.setUp();&lt;br /&gt;        setupOpenCms(&quot;simpletest&quot;, &quot;/sites/default/&quot;);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void testExistingResource() throws Exception {&lt;br /&gt;        CmsObject cms = getCmsObject();&lt;br /&gt;        CmsResource res = cms.readResource(&quot;/index.html&quot;);&lt;br /&gt;        assertEquals(&quot;/sites/default/index.html&quot;, res.getRootPath());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void tearDown() throws Exception {&lt;br /&gt;        super.tearDown();&lt;br /&gt;        removeOpenCms();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;</content>
 </entry>
 
 <entry>
   <title>Unicode is not UTF-8</title>
   <link href="http://blog.florian-hopf.de/2010/04/unicode-is-not-utf-8.html"/>
   <updated>2010-04-10T19:18:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2010/04/unicode-is-not-utf-8</id>
   <content type="html">Problems with encoding are common on a lot of projects I worked on. Sometimes I tend to get the feeling that I understand most of it but then there are always aspects I did not get right. This week I noticed that even my basic knowledge is not really firm.  &lt;br /&gt;&lt;br /&gt;Currently I am working on a system where we do a lot of imports from other systems that provide data as XML. The company that delivers the data sent us some sample data that we tried to import. The XML document was supposed to be in &lt;a href=&quot;http://en.wikipedia.org/wiki/UTF-8&quot;&gt;UTF-8&lt;/a&gt; but somehow our parser always choked on some byte sequences. When we added iso-8859-1 to the xml prolog the parsing was working fine but all non-ASCII characters where not displayed correctly.&lt;br /&gt;&lt;br /&gt;Using hexedit I looked at the document and located the values for non-ASCII characters like 'ä' which is displayed as 'C3 A4' in hex. But looking it up in the &lt;a href=&quot;http://unicode.org/charts/PDF/U0080.pdf&quot;&gt;Unicode code chart&lt;/a&gt; it should be the value '00 E4'. We complained that the data seemed to be send in a different encoding but not UTF-8.&lt;br /&gt;&lt;br /&gt;Of course the company could not find any problem because we were just wrong. Unicode is not UTF-8! UTF-8 is an encoding scheme which is used to encode unicode characters but the byte values do not match. &lt;br /&gt;&lt;br /&gt;Let's analyze the example character 'ä' in a UTF-8 document. It displays as 'C3 A4'. In binary format this is:&lt;br /&gt;&lt;pre&gt;1100 0011 1010 0100&lt;/pre&gt;&lt;br /&gt;UTF-8 uses a start byte and one or more continuation bytes. A start byte is identified by two leading '11' which makes the first byte our start byte. Continuation bytes are identified by a leading '10', so the second byte is a continuation byte. These are the control bits that are used by UTF-8. Let's see what our sequence looks like if we just remove these control bits, shift the bits together and pad the left side with 0:&lt;br /&gt;&lt;pre&gt;0000 0000 1110 0100&lt;/pre&gt; &lt;br /&gt;Of course this is the expected Unicode value '00 E4' for 'ä'.&lt;br /&gt;&lt;br /&gt;Very basic, but I still managed to get it wrong. &lt;br /&gt;&lt;br /&gt;Later a colleague noticed that in some part of our application a String was created from a byte array without specifying an explicit encoding. Ouch! Finally it was fixed quickly but we should have looked at our code first before blaming the data provider.</content>
 </entry>
 
 <entry>
   <title>Playing with Groovy</title>
   <link href="http://blog.florian-hopf.de/2010/04/playing-with-groovy.html"/>
   <updated>2010-04-09T15:12:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2010/04/playing-with-groovy</id>
   <content type="html">As I am the one who does most of our &lt;a href=&quot;http://www.opencms.org/&quot;&gt;OpenCms&lt;/a&gt; projects I am also the one who has to deploy new versions to our internal &lt;a href=&quot;http://nexus.sonatype.org/&quot;&gt;Nexus&lt;/a&gt; repository so we can easily use the libraries from &lt;a href=&quot;http://maven.apache.org/&quot;&gt;Maven&lt;/a&gt;. &lt;a href=&quot;http://www.alkacon.com/&quot;&gt;The guys developing OpenCms&lt;/a&gt; use Ant for their builds so there is no official Maven repository available.&lt;br /&gt;&lt;br /&gt;Most of the time I added only the dependencies that are really necessary on compile time because creating a Maven POM by hand is quite cumbersome, not to mention the deployment of all the dependencies (either uploading to Nexus or deploying using Maven). One of those time consuming tasks that needs some automation.&lt;br /&gt;&lt;br /&gt;I chose &lt;a href=&quot;http://groovy.codehaus.org/&quot;&gt;Groovy&lt;/a&gt; for implementing a little helper script because it is really good at dealing with XML and it's always good to learn some new techniques. I already got some experience in modifying existing scripts but did not use it for creating something from scratch.&lt;br /&gt;&lt;br /&gt;The script basically just reads a folder with jars and creates a pom for the whole project as well as a script for deploying all additional artifacts using Maven. Creating the files currently involves two steps:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;A properties file is created from the information that is guessed from the filename of the jars. As jars are often not named consistent this will not succeed for all jars. So you have to review the file and change some group names, artifactIds and versions (another step that could be automated, e.g. by querying a Nexus instance, but let's save some work for the future ;) ). Another properties file holds project and deployment information like project groupId and artifactId and the server to deploy to.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The properties files are read again by another script and the project pom as well as the deployment script is generated.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;Of course you also have to review the deployment script because you don't want to deploy any artifacts that are already available. A good way to find out which artifacts are missing is to call something like {{{mvn compile}}} on the generated pom.&lt;br /&gt;I uploaded the &lt;a href=&quot;http://synyx.org/system/galleries/download/external/DeploymentHelper.zip&quot;&gt;(uncommented) scripts and helper classes&lt;/a&gt;, maybe it's useful for somebody.&lt;br /&gt;&lt;br /&gt;I guess there are far better solutions to creating maven projects from existing libraries, I am looking forward to hearing about them.&lt;br /&gt;&lt;br /&gt;The XML manipulation features of Groovy are really nice. When writing XML you append your node structure to a builder object and it creates the markup for you. You always stay very close to the format you intend to output. E.g. this is the code to create the XML for a pom file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def writer = new StringWriter();&lt;br /&gt;def xmlBuilder = new MarkupBuilder(writer);&lt;br /&gt;&lt;br /&gt;xmlBuilder.project('xmlns' : 'http://maven.apache.org/POM/4.0.0',&lt;br /&gt;  'xmlns:xsi' : 'http://www.w3.org/2001/XMLSchema-instance',&lt;br /&gt;  'xsi:schemaLocation' : 'http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd') {&lt;br /&gt;  modelVersion('4.0.0')&lt;br /&gt;  groupId(project.group)&lt;br /&gt;  artifactId(project.artifact)&lt;br /&gt;  packaging('jar')&lt;br /&gt;  version(project.version)&lt;br /&gt;  name(project.artifact)&lt;br /&gt;  dependencies() {&lt;br /&gt;      dependencies.each() { dep -&gt;&lt;br /&gt;          dependency {&lt;br /&gt;              groupId(dep.group)&lt;br /&gt;              artifactId(dep.artifact)&lt;br /&gt;              version(dep.version)&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You are looking at this code and immediately can imagine the structure of the resulting XML. I like it!&lt;br /&gt;&lt;br /&gt;Also, file manipulation is really nice. No need to do any resource cleanup. This code is responsible for writing the created pom file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;new File(dir + &quot;pom.xml&quot;).withWriter() { out -&gt;&lt;br /&gt;   out.println(projectPom.toString());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Some drawbacks I noticed when doing scripting like this:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;I tend to get sloppy while coding. Not adding semicolons to the end of lines, doing too many things in one class/script, ...&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Code completion in Netbeans is horrible if you are used to Java standards, but I guess that is just very hard to implement&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You have to compile manually, at least in Netbeans. If you are changing a Groovy class that is used by a script you have to remember to do a build because when running the script Netbeans will not compile it for you.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A lot of coding errors are only discovered on runtime. E.g. using the wrong name for properties or calling a constructor that doesn't exist&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I still can't imagine using a dynamic language on production code. Of course the deployment time tends to be shorter but still you have to execute the code to see if it is really correct. I doubt that writing tests could compensate for the lack of static type checking but probably this needs a shift of the mindset.</content>
 </entry>
 
 <entry>
   <title>Fun with TreeMap</title>
   <link href="http://blog.florian-hopf.de/2010/03/fun-with-treemap.html"/>
   <updated>2010-03-15T15:45:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2010/03/fun-with-treemap</id>
   <content type="html">&lt;a href=&quot;http://api.synyx.de/j2sdk6/api/java/util/TreeMap.html&quot;&gt;TreeMap&lt;/a&gt; is the only implementation of a &lt;a href=&quot;http://api.synyx.de/j2sdk6/api/java/util/SortedMap.html&quot;&gt;SortedMap&lt;/a&gt; that is included in the JDK. It stores its values in a &lt;a href=&quot;http://en.wikipedia.org/wiki/Red_black_tree&quot;&gt;Red-black tree&lt;/a&gt;, therefore its entries can be accessed, inserted and deleted really fast. &lt;br /&gt;&lt;br /&gt;Unfortunately, using the class is not really intuitive, so you better read the API docs carefully or prepare to spend hours of your time chasing mysterious bugs.&lt;br /&gt;&lt;br /&gt;Imagine that you want to use a TreeMap with String keys and String values that is ordered according to the length of the key in a way so that the longest key comes first. Should be quite simple, just implement a comparator that does the work for you:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import java.util.Comparator;&lt;br /&gt;&lt;br /&gt;public class StringLengthComparator implements Comparator&amp;lt;String&amp;gt; {&lt;br /&gt;&lt;br /&gt;    public int compare(String o1, String o2) {&lt;br /&gt;        // if both lengths are the same return 0&lt;br /&gt;        return o2.length() - o1.length();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;If both Strings have the same length we return 0 and a negative or positive value otherwise. As I always get confused when to return a positive value and when a negative let's create a simple test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    Map&amp;lt;String, String&amp;gt; map;&lt;br /&gt;    &lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        map = new TreeMap&amp;lt;String, String&amp;gt;(new StringLengthComparator());&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    @Test&lt;br /&gt;    public void testValuesWithDifferentLength() {&lt;br /&gt;&lt;br /&gt;        map.put(&quot;zero&quot;, &quot;0&quot;);&lt;br /&gt;        map.put(&quot;one&quot;, &quot;1&quot;);&lt;br /&gt;        map.put(&quot;three&quot;, &quot;3&quot;);&lt;br /&gt;&lt;br /&gt;        assertThat(&quot;Length failed&quot;, map.size(), is(3));&lt;br /&gt;&lt;br /&gt;        Iterator&amp;lt;String&amp;gt; it = map.keySet().iterator();&lt;br /&gt;        assertThat(it.next(), is(&quot;three&quot;));&lt;br /&gt;        assertThat(it.next(), is(&quot;zero&quot;));&lt;br /&gt;        assertThat(it.next(), is(&quot;one&quot;));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;  &lt;br /&gt;If we run this test everything seems to be ok;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Testsuite: StringLengthComparatorTest&lt;br /&gt;Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0,018 sec&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But what happens if we just insert the missing two? Lets see:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testDifferentValuesWithSameLength() {&lt;br /&gt;&lt;br /&gt;        map.put(&quot;zero&quot;, &quot;0&quot;);&lt;br /&gt;        map.put(&quot;one&quot;, &quot;1&quot;);&lt;br /&gt;        map.put(&quot;two&quot;, &quot;2&quot;);&lt;br /&gt;        map.put(&quot;three&quot;, &quot;3&quot;);&lt;br /&gt;&lt;br /&gt;        assertThat(&quot;Length failed&quot;, map.size(), is(4));&lt;br /&gt;&lt;br /&gt;        Iterator&amp;lt;String&amp;gt; it = map.keySet().iterator();&lt;br /&gt;        // just check the first two values as we do not know the order of the same length&lt;br /&gt;        assertThat(it.next(), is(&quot;three&quot;));&lt;br /&gt;        assertThat(it.next(), is(&quot;zero&quot;));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This test should also pass, we just can't tell the ordering of the same length keys:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Testcase: testDifferentValuesWithSameLength(StringLengthComparatorTest):        FAILED&lt;br /&gt;Length failed&lt;br /&gt;Expected: is &lt;4&gt;&lt;br /&gt;     got: &lt;3&gt;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;Oops ... what happened here? One of the values just disappeared? We are inserting 4 entries but there are only 3 in the Map?&lt;br /&gt;&lt;br /&gt;The first time I stumbled across this it took me several hours to figure out what was going on. After I found out what the problem was it should have taught me to always read the javadoc carefully. The contract for TreeMap states the following:&lt;br /&gt;&lt;blockquote&gt;Note that the ordering maintained by a sorted map (whether or not an explicit comparator is provided) must be consistent with equals if this sorted map is to correctly implement the Map interface.&lt;/blockquote&gt;&lt;br /&gt;What exactly does this mean? &lt;a href=&quot;http://api.synyx.de/j2sdk6/api/java/util/Comparator.html&quot;&gt;Comparator&lt;/a&gt; tells us more about consistent with equals:&lt;br /&gt;&lt;blockquote&gt;The ordering imposed by a comparator c on a set of elements S is said to be consistent with equals if and only if c.compare(e1, e2)==0 has the same boolean value as e1.equals(e2) for every e1 and e2 in S.&lt;/blockquote&gt;&lt;br /&gt;Normally, the Map- and Set-Interface use equals to determine if a certain key (for a Map) or a certain value (for a Set) is already inserted. But for TreeMap and TreeSet this is not the case. These implementations use compareTo for determining whether an Object is equal to another Object. This means you are not allowed to return 0 from a compare-method if the two Objects are not equal! &lt;br /&gt;&lt;br /&gt;How to fix it? Just test if the two Strings are equal before comparing the length in the Comparator:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    public int compare(String o1, String o2) {&lt;br /&gt;&lt;br /&gt;        // return 0 only if both are equal&lt;br /&gt;        if (o1.equals(o2)) {&lt;br /&gt;            return 0;&lt;br /&gt;        } else if (o1.length() &gt; o2.length()) {&lt;br /&gt;            return -1;&lt;br /&gt;        } else {&lt;br /&gt;            return 1;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Alright, the test passes. Just to be sure we add another test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testYetMoreValues() {&lt;br /&gt;&lt;br /&gt;        map.put(&quot;zero&quot;, &quot;0&quot;);&lt;br /&gt;        map.put(&quot;one&quot;, &quot;1&quot;);&lt;br /&gt;        map.put(&quot;four&quot;, &quot;4&quot;);&lt;br /&gt;&lt;br /&gt;        assertThat(&quot;Length failed&quot;, map.size(), is(3));&lt;br /&gt;&lt;br /&gt;        assertNotNull(&quot;Zero's not there&quot;, map.get(&quot;zero&quot;));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Seems to be a stupid test but let's see what happens:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Testsuite: StringLengthComparatorTest&lt;br /&gt;Tests run: 3, Failures: 1, Errors: 0, Time elapsed: 0,027 sec&lt;br /&gt;&lt;br /&gt;Testcase: testYetMoreValues(StringLengthComparatorTest):        FAILED&lt;br /&gt;Zero's not there&lt;br /&gt;junit.framework.AssertionFailedError: Zero's not there&lt;br /&gt;        at StringLengthComparatorTest.testYetMoreValues(StringLengthComparatorTest.java:51)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;How can this happen? We tested for equality but the &quot;zero&quot; value still is not there? Let's see what the tree looks like on every step.&lt;br /&gt;&lt;br /&gt;Still simple if the first value (&quot;zero&quot;) is inserted: The entry is the root node:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://3.bp.blogspot.com/_XftA4GBHqKs/S51uhwesJPI/AAAAAAAAAAM/bd-8jJBcxeA/s1600-h/tree1.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 192px; height: 128px;&quot; src=&quot;http://3.bp.blogspot.com/_XftA4GBHqKs/S51uhwesJPI/AAAAAAAAAAM/bd-8jJBcxeA/s320/tree1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5448632650416530674&quot; /&gt;&lt;/a&gt;&lt;br /&gt;When inserting &quot;one&quot; afterwards, the Comparator tells that the result of the comparison is 1, which means that the node has to be inserted on the right side:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://2.bp.blogspot.com/_XftA4GBHqKs/S51xiAUmLNI/AAAAAAAAAAU/6JZCMElkpWI/s1600-h/tree2.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align: center; cursor:pointer; cursor:hand;width: 265px; height: 153px;&quot; src=&quot;http://2.bp.blogspot.com/_XftA4GBHqKs/S51xiAUmLNI/AAAAAAAAAAU/6JZCMElkpWI/s320/tree2.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5448635953204047058&quot; /&gt;&lt;/a&gt;&lt;br /&gt;When inserting &quot;four&quot;, the tree looks a little bit different. As our Comparator in this case also return 1, the node is also inserted right of &quot;zero&quot; but needs to be left of &quot;one&quot;. &quot;four&quot; is our new root node:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://1.bp.blogspot.com/_XftA4GBHqKs/S51xtU7sTzI/AAAAAAAAAAc/9nbjnynNA30/s1600-h/tree3.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 282px; height: 143px;&quot; src=&quot;http://1.bp.blogspot.com/_XftA4GBHqKs/S51xtU7sTzI/AAAAAAAAAAc/9nbjnynNA30/s320/tree3.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5448636147715297074&quot; /&gt;&lt;/a&gt;&lt;br /&gt;Seems to look ok. When reading from left to right the nodes are sorted according to their length. But let's see what happens when we try to look up &quot;zero&quot; based on the tree above. We start with the root node &quot;four&quot;. As the length of &quot;zero&quot; and &quot;four&quot; is the same, our Comparator returns 1. But on the right side it only finds the &quot;one&quot; node. Our comparator returns -1 when comparing &quot;four&quot; to &quot;one&quot;. Nothing there, so null is returned.  &lt;br /&gt;&lt;br /&gt;The Javadoc for Comparator tells us more on what went wrong here:&lt;br /&gt;&lt;blockquote&gt;The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y.&lt;/blockquote&gt;&lt;br /&gt;This means that the method needs to be implemented symetrically. When calling compare(&quot;zero&quot;, &quot;one&quot;) we always need to return the negated value of calling compare(&quot;one&quot;, &quot;zero&quot;). With our implementation this is not the case as we always return 1 as a fallback.&lt;br /&gt;&lt;br /&gt;I hope this is a final implementation of the Comparator delegating to Strings compareTo()-method which is already implemented symetrically:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    public int compare(String o1, String o2) {&lt;br /&gt;&lt;br /&gt;        // return 0 only if both are equal&lt;br /&gt;        if (o1.equals(o2)) {&lt;br /&gt;            return 0;&lt;br /&gt;        } else if (o1.length() == o2.length()) {&lt;br /&gt;            // delegate to Strings compareTo to get symmetric behavior&lt;br /&gt;            return o1.compareTo(o2);&lt;br /&gt;        } else if (o1.length() &gt; o2.length()) {&lt;br /&gt;            return -1;&lt;br /&gt;        } else {&lt;br /&gt;            return 1;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I struggled with both of these problems on two different projects and it always took me some hours. Thanks to my colleague Marc who led me to the solution of the symmetric problem.</content>
 </entry>
 
 <entry>
   <title>Implementing an API abstraction using Google Collections</title>
   <link href="http://blog.florian-hopf.de/2009/11/implementing-api-abstraction-using.html"/>
   <updated>2009-11-19T20:17:00+08:00</updated>
   <id>http://blog.florian-hopf.de//blog/2009/11/implementing-api-abstraction-using</id>
   <content type="html">I am using &lt;a href=&quot;http://opencms.org/&quot;&gt;OpenCms&lt;/a&gt;, the open source content management system, quite often. It is a good choice for building structured medium to large sized websites. One drawback though sometimes is the layout of the APIs. E.g. for accessing the resources managed in the system programatically you have to use some final and nonfinal classes that depend on a running instance of OpenCms. The original developers very likely chose this approach for security reasons but this can become quite cumbersome, e.g. when dealing with tests, as you cannot mock some of these classes easily. There is an integration test facility that comes with OpenCms but as this starts an OpenCms instance every time a test is executed, running tests takes some time. I tend to write tests that work without a running system whenever possible.&lt;br /&gt;&lt;br /&gt;To improve testability of my components I implemented a thin layer above the normal OpenCms access means using some interfaces and simple POJOs so that my business logic can be tested without starting OpenCms. One interface and its default implementation act as a kind of DAO for accessing the virtual filesystem of OpenCms. Its method signatures do not contain any OpenCms dependencies that can't be mocked or reconstructed easily. E.g. to represent file resources the OpenCms class normally used is &lt;code&gt;CmsResource&lt;/code&gt;. This class is quite difficult to instanciate outside of a running OpenCms instance as it contains internal references to different database tables. To reduce the need for mocking these external classes I implemented a simple POJO, &lt;code&gt;Resource&lt;/code&gt;, that contains relevant information like the path to the resource and it's type.&lt;br /&gt;&lt;br /&gt;Some methods of my VFS DAO return a &lt;code&gt;Collection&lt;/code&gt; of &lt;code&gt;Resource&lt;/code&gt;s, e.g. when reading all resources in a subfolder. As the OpenCms API returns an untyped &lt;code&gt;List&lt;/code&gt; that contains &lt;code&gt;CmsResource&lt;/code&gt;s and in my interface method signature I use &lt;code&gt;List&amp;lt;? extends Resource&amp;gt;&lt;/code&gt; some transformation needs to take place. In the first project I used the abstraction I implemented it in a really simple way:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;List&amp;lt;Resource&amp;gt; resources = new ArrayList&amp;lt;Resource&amp;gt;();&lt;br /&gt;@SupressWarnings(&quot;Unchecked&quot;)&lt;br /&gt;List&amp;lt;CmsResources&amp;gt; cmsResources = cms.readResources(...);&lt;br /&gt;for (CmsResource cmsResource: cmsResources) {&lt;br /&gt; resources.add(transform(cmsResource);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The transform method just creates an instance of the &lt;code&gt;Resource&lt;/code&gt; and fills it with the needed values.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private Resource transform(CmsResource cmsResource) {&lt;br /&gt; Resource resource = new Resource();&lt;br /&gt; resource.setDateLastModified(cmsResource.getDateLastModified());&lt;br /&gt; ...&lt;br /&gt; return resource;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This approach works and in my opinion is ok to use in many circumstances. I sacrificed some performance for a gain in testability and design. But for large collections or operations that are triggered frequently this of course can become a performance issue as for the sake of abstraction it is necessary to iterate the collection.&lt;br /&gt;&lt;br /&gt;The better solution is to use a lazy list that transforms the &lt;code&gt;CmsResource&lt;/code&gt;s on the fly to &lt;code&gt;Resource&lt;/code&gt;s. With a lazy list you don't have to iterate it when transforming. The transformation happens when you are accessing the list.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://code.google.com/p/google-collections/&quot;&gt;Google Collections&lt;/a&gt; provides a functional style approach for transforming lists lazily. You create a class that implements the interface &lt;code&gt;Function&lt;/code&gt; that can be typed for the source and target. In its &lt;code&gt;apply&lt;/code&gt; method the transformation step is implemented in basically the same way as in the method displayed above.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class ResourceTransformationFunction implements Function&lt;cmsresource, resource=&quot;&quot;&gt; {&lt;br /&gt;&lt;br /&gt;   public Resource apply(CmsResource cmsResource) {&lt;br /&gt;       Resource resource = new Resource();&lt;br /&gt;       resource.setDateCreated(cmsResource.getDateCreated());&lt;br /&gt;       resource.setDateLastModified(cmsResource.getDateLastModified());&lt;br /&gt;       ...&lt;br /&gt;       return resource;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The original &lt;code&gt;List&lt;/code&gt; is transfomed using a static method call that accepts an instance of our &lt;code&gt;Function&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@SuppressWarnings(&quot;unchecked&quot;)&lt;br /&gt;List&amp;lt;CmsResource&amp;gt; cmsResources = cms.readResources(...);&lt;br /&gt;List&amp;lt;Resource&amp;gt; resources =  Lists.transform(cmsResources, new ResourceTransformationFunction());&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The transformation happens when the &lt;code&gt;List&lt;/code&gt; is accessed so when you are iterating the collection only once, which should be the case in most applications, there is no overhead at all (besides the creation of the new objects).&lt;br /&gt;&lt;br /&gt;I really like the ease of use and reusabilty of the Google Collections solution. Also, the jar comes with absolutely no dependencies which makes it easily embedabble in any project.&lt;br /&gt;&lt;br /&gt;A similiar functional approach will be part of the new concurrency features in JDK 7. &lt;code&gt;ParallelArray&lt;/code&gt;, which makes use of the Fork/Join framework will provide the ability to use functions and predicates when constructing arrays. Brian Goetz' &lt;a href=&quot;http://parleys.com/display/PARLEYS/Home#slide=1;talk=24576007;title=From%20Concurrent%20to%20Parallel&quot;&gt;talk&lt;/a&gt; on Devoxx 2008 contained a detailed introduction to these features.&lt;br /&gt;&lt;br /&gt;On Devoxx 2009, Dick Wall of the &lt;a href=&quot;http://javaposse.com/&quot;&gt;Javaposse&lt;/a&gt; held a really good talk about appliying a more functional style of programming to the Java programming language. This talk will be available some time in the future at &lt;a href=&quot;http://parleys.com&quot;&gt;parleys.com&lt;/a&gt;</content>
 </entry>
 

</feed>
