<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Search Nuggets &#187; Solr</title>
	<atom:link href="http://blog.comperiosearch.com/blog/category/tech/solr/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.comperiosearch.com</link>
	<description>A blog about Search as THE solution</description>
	<lastBuildDate>Mon, 13 Jun 2016 08:59:45 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>Sitevision – förbättra söket med Nutch</title>
		<link>http://blog.comperiosearch.com/blog/2016/06/08/sitevision-forbattra-soket-med-nutch/</link>
		<comments>http://blog.comperiosearch.com/blog/2016/06/08/sitevision-forbattra-soket-med-nutch/#comments</comments>
		<pubDate>Wed, 08 Jun 2016 14:20:07 +0000</pubDate>
		<dc:creator><![CDATA[Jack Thorén]]></dc:creator>
				<category><![CDATA[Nutch]]></category>
		<category><![CDATA[Sitevision]]></category>
		<category><![CDATA[Solr]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[CRM]]></category>
		<category><![CDATA[web crawler]]></category>

		<guid isPermaLink="false">http://blog.comperiosearch.com/?p=4096</guid>
		<description><![CDATA[Ett av Sveriges mest populära CMS verktyg är Sitevision, som används kanske främst av stora statliga myndigheter och kommuner. Valet att använda sig av Sitevision hos dessa myndigheter och kommuner är nog att det är väldigt enkelt för redaktörer och sidansvariga att använda och att underhålla informationen på sina sidor. Detta i en miljö där [...]]]></description>
				<content:encoded><![CDATA[<p>Ett av Sveriges mest populära CMS verktyg är Sitevision, som används kanske främst av stora statliga myndigheter och kommuner. Valet att använda sig av Sitevision hos dessa myndigheter och kommuner är nog att det är väldigt enkelt för redaktörer och sidansvariga att använda och att underhålla informationen på sina sidor. Detta i en miljö där kanske den webbtekniska kunskapen inte är på samma nivå som hos ett större teknikföretag.</p>
<p>Men medans vi hyllar det enkla användargränssnittet så önskar vi att det gick att bygga bättre sökfunktionalitet. Visst kan du söka i en webbsajt, det går även att söka på andra webbsajter, om du har satt upp flera webbsajter inom samma system. Men om du vill söka i en webbsajt eller databas som finns någon annanstans då går det inte. Men detta är på väg att ändras. Sitevision introducerar snart webbkravlaren Nutch, en mycket avancerad webcrawler som bygger på Hadoop, som i sin tur är del av ett ramverk för att hantera mycket stora mängder data. Nutch tillsammans med Solr kommer att lyfta Sitevisions sök till nya höjder.</p>
<p><strong>Nedan är ett schema för hur en sajt-indexering skulle kunna se </strong><strong>ut:</strong></p>
<p><a href="http://blog.comperiosearch.com/wp-content/uploads/2016/06/Jack_blog011.png"><img class="alignnone  wp-image-4098" src="http://blog.comperiosearch.com/wp-content/uploads/2016/06/Jack_blog011.png" alt="Jack_blog01" width="605" height="287" /></a></p>
<p>1. ”Injector” tar alla webbadresser i nutch.txt filen och lägger till dem i ”CrawlDB&#8221;. Som är en central del av Nutch. CrawlDB innehåller information om alla kända webbadresser (hämta schema, hämta status, metadata, &#8230;).</p>
<p>2. Baserat på data från CrawlDB skapar ”Generator” en lista på vad som ska hämtas och placerar det i en nyskapad segment katalog.</p>
<p>3. Nästa steg, ”Fetcher” får de adresser som ska hämtas från listan och skriver det tillbaka till segment katalogen. Detta steg är vanligtvis den mest tidskrävande delen.</p>
<p>4. Nu kan ”Parser” behandla innehållet i varje webbsida och exempelvis utelämnar alla html-taggar. Om denna hämtning (crawl) är en uppdatering eller en utökning av en redan tidigare hämtning (t.ex. djup 3), skulle ”Updater” lägga till nya data till CrawlDB som ett nästa steg.</p>
<p>5. Före indexering måste alla länkar inverteras av ”Link Inverter”, som tar hänsyn till att inte antalet utgående länkar på en webbsida är av intresse, utan snarare antalet inkommande länkar. Detta är ganska likt hur Google Pagerank fungerar och är viktig för scoring funktion. De inverterade länkarna sparas i “Linkdb”.</p>
<p>6-7. Med hjälp av data från alla möjliga källor (CrawlDB, LinkDB och segment), skapar Indexer ett index och sparar det i Solr katalogen. För indexering, används det populära Lucene biblioteket. Nu kan användaren söka efter information om genomsökta webbsidor via Solr.</p>
<p><strong>Funktionalitet som följer med Nutch:</strong></p>
<ul>
<li>Indexering av externa källor</li>
<li>Automatisk kategorisering</li>
<li>Metadata</li>
<li>Textanalys</li>
<li>Utökad funktionalitet enkelt med plugins</li>
</ul>
<p><strong>Nyttiga länkar:</strong></p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Apache_Nutch">https://en.wikipedia.org/wiki/Apache_Nutch</a></li>
<li><a href="http://wiki.apache.org/nutch/">http://wiki.apache.org/nutch/</a></li>
<li><a href="http://nutch.apache.org/">http://nutch.apache.org/</a></li>
<li><a href="http://www.sitevision.se/vara-produkter/sitevision.html">http://www.sitevision.se/vara-produkter/sitevision.html</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.comperiosearch.com/blog/2016/06/08/sitevision-forbattra-soket-med-nutch/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Analysing Solr logs with Logstash</title>
		<link>http://blog.comperiosearch.com/blog/2015/09/21/solr-logstash-analysis/</link>
		<comments>http://blog.comperiosearch.com/blog/2015/09/21/solr-logstash-analysis/#comments</comments>
		<pubDate>Sun, 20 Sep 2015 22:00:00 +0000</pubDate>
		<dc:creator><![CDATA[Seb Muller]]></dc:creator>
				<category><![CDATA[Elasticsearch]]></category>
		<category><![CDATA[Solr]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[analysis]]></category>
		<category><![CDATA[grok]]></category>
		<category><![CDATA[logs]]></category>
		<category><![CDATA[logstash]]></category>

		<guid isPermaLink="false">http://blog.comperiosearch.com/?p=3934</guid>
		<description><![CDATA[Analysing Solr logs with Logstash Although I usually write about and work with Apache Solr, I also use the ELK stack on a daily basis on a number of projects. If you&#8217;re not familiar with Solr, take a look at some of my previous posts. If you need some more background info on the ELK [...]]]></description>
				<content:encoded><![CDATA[<h1>Analysing Solr logs with Logstash</h1>
<p>Although I usually write about and work with <a href="http://lucene.apache.org/solr/">Apache Solr</a>, I also use the <a href="https://www.elastic.co/downloads">ELK stack</a> on a daily basis on a number of <a>projects.</a> If you&#8217;re not familiar with Solr, take a look at some of my <a href="http://blog.comperiosearch.com/blog/author/sebm/">previous posts</a>. If you need some more background info on the ELK stack, both <a href="http://blog.comperiosearch.com/blog/author/cvig/">Christoffer</a> and <a href="http://blog.comperiosearch.com/blog/author/alynum/">André</a> have written many great posts on various ELK subjects. The most common use for the stack is data analysis. In our case, Solr search log analysis.</p>
<p>As a little side note for the truly devoted Solr users, an ELK stack alternative exists with <a href="http://lucidworks.com/fusion/silk/">SiLK</a>. I highly recommend checking out Lucidworks&#8217; various blog posts on <a href="http://lucidworks.com/blog/">Solr and search in general</a>.</p>
<h2>Some background</h2>
<p>On an existing search project I use the ELK stack to ingest, analysis and visualise logs from Comperio&#8217;s search middleware application.<br />
<a href="http://blog.comperiosearch.com/wp-content/uploads/2088/09/search_logs.png"><img class="aligncenter size-medium wp-image-3942" src="http://blog.comperiosearch.com/wp-content/uploads/2088/09/search_logs-300x157.png" alt="Search Logs Dashboard" width="300" height="157" /></a><br />
Although this gave us a great view of user query behaviour, Solr logs a great more detailed information. I wanted to log indexing events, errors and the searches with all other parameters in addition to just the query string.</p>
<h2>Lets get started</h2>
<p>I&#8217;m going to assume you already have a running Solr installation. You will, however, need to download <a href="https://www.elastic.co/products/elasticsearch">Elasticsearch</a> and <a href="https://www.elastic.co/products/logstash">Logstash</a> and unpack them. Before we start Elasticsearch, I recommend installing these plugins:</p>
<ul>
<li><a href="http://mobz.github.io/elasticsearch-head/">Head</a></li>
<li><a href="https://www.elastic.co/guide/en/marvel/current/_installation.html">Marvel</a></li>
</ul>
<p>Head is a cluster health monitoring tool. Marvel we&#8217;ll only need for the bundled developer console, Sense. To disable Marvel&#8217;s other capabilities, add this line to ~/elasticsearch/config/elasticsearch.yml</p><pre class="crayon-plain-tag">marvel.agent.enabled: false</pre><p>Start elasticsearch with this command:</p><pre class="crayon-plain-tag">~/elasticsearch-[version]/bin/elasticsearch</pre><p>Navigate to <a href="http://localhost:9200/">http://localhost:9200/</a> to confirm that Elasticsearch is running. Check <a href="http://localhost:9200/_plugin/head">http://localhost:9200/_plugin/head</a> and <a href="http://localhost:9200/_plugin/marvel/sense/index.html">http://localhost:9200/_plugin/marvel/sense/index.html</a> to verify the plugins installed correctly.</p>
<h2>The anatomy of a Logstash config</h2>
<hr />
<h3>Update 21/09/15</h3>
<p>I have since greatly simplified the multiline portions of the Logstash configs. Use instead this filter section: <script src="https://gist.github.com/41ca2c34c50d0d9d8e82.js?file=solr-filter.conf"></script>The rest of the original article contents are unchanged for comparison&#8217;s sake.</p>
<hr />
<p>All Logstash configs share three main building blocks. It starts with the Input stage, which defines what the data source is and how to access it. Next is the Filter stage, which carries out data processing and extraction. Finally, the Output stage tells Logstash where to send the processed data. Lets start with the basics, the input and output stages:</p><pre class="crayon-plain-tag">input {
  file {
    path =&gt; "~/solr.log"
  }
}

filter {}

output {
  # Send directly to local Elasticsearch
  elasticsearch_http {
    host =&gt; "localhost"
    template =&gt; "~/logstash/bin/logstash_solr_template.json"
    index =&gt; "solr-%{+YYYY.MM.dd}"
    template_overwrite =&gt; true
  }</pre><p>This is one of the simpler input/output configs. We read a file at a given location and stream its raw contents to an Elasticsearch instance. Take a look at the <a href="https://www.elastic.co/guide/en/logstash/current/input-plugins.html">input</a> and <a href="https://www.elastic.co/guide/en/logstash/current/output-plugins.html">output</a> plugins&#8217; documentation for more details and default values. The index setting causes Logstash to create a new index every day with a name generated from the provided pattern. The template option tells Logstash what kind of field mapping and settings to use when creating the Elasticsearch indices. You can find the template file I used <a href="https://gist.github.com/sebnmuller/41ca2c34c50d0d9d8e82#file-solr-template-json">here</a>.</p>
<p>To process the Solr logs, we&#8217;ll use the <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html">grok</a>, <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-mutate.html">mutate</a>, <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-multiline.html">multiline</a>, <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-drop.html">drop</a> and <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-kv.html">kv</a> filter plugins.</p>
<ul>
<li>Grok is a regexp based parsing stage primarily used to match strings and extract parts. There are a number of default patterns described on the grok documentation page. While building your grok expressions, the <a href="https://grokdebug.herokuapp.com/">grok debugger app</a> is particularly helpful. Be mindful though that some of the escaping syntax isn&#8217;t always the same in the app as what the Logstash config expects.</li>
<li>We need the multiline plugin to link stacktraces to their initial error message.</li>
<li>The kv, aka key value, plugin will help us extract the parameters from Solr indexing and search events</li>
<li>We use mutate to add and remove tags along the way.</li>
<li>And finally, drop to drop any events we don&#8217;t want to keep.</li>
</ul>
<p>&nbsp;</p>
<h2>The <del>hard</del> fun part</h2>
<p>Lets dive into the filter stage now. Take a look at the <a href="https://gist.github.com/sebnmuller/41ca2c34c50d0d9d8e82#file-solr-logstash-conf">config file</a> I&#8217;m using. The Grok patterns may appear a bit daunting, especially if you&#8217;re not very familiar with regexp and the default Grok patterns, but don&#8217;t worry! Lets break it down.</p>
<p>The first section extracts the log event&#8217;s severity and timestamp into their own fields, &#8216;level&#8217; and &#8216;LogTime&#8217;:</p><pre class="crayon-plain-tag">grok {
    match =&gt; { "message" =&gt; "%{WORD:level}.+?- %{DATA:LogTime};" }
      tag_on_failure =&gt; []
  }</pre><p>So, given this line from my <a href="https://gist.github.com/sebnmuller/41ca2c34c50d0d9d8e82#file-solr-log">example log file</a></p><pre class="crayon-plain-tag">INFO  - 2015-09-07 15:40:34.535; org.apache.solr.update.processor.LogUpdateProcessor; [sintef_main] webapp=/ path=/update/extract params={literal.source=epifile&amp;literal.epi_file_title=GOFER+L4.0+Demonstratorer+V1.0.pdf&amp;literal.title=GOFER+L4.0+Demonstratorer+V1.0.pdf&amp;literal.id=epifile_211278&amp;literal.epifileid_s=211278&amp;literal.url=http://www.sintef.no/globalassets/upload/teknologi_samfunn/6060/prosjektfiler/gofer/gofer-l4.0-demonstratorer-v1.0.pdf&amp;stream.url=http://www.sintef.no/globalassets/upload/teknologi_samfunn/6060/prosjektfiler/gofer/gofer-l4.0-demonstratorer-v1.0.pdf&amp;literal.filesource_s=SiteFile} {} 0 65</pre><p>We&#8217;d extract</p><pre class="crayon-plain-tag">{ "level": "INFO", "LogTime":"2015-09-07 15:40:34.535"}</pre><p>In the template file I linked earlier, you&#8217;ll notice configuration for the LogTime field. Here we define for Elasticsearch a valid DateTime format. We need to do this so that Kibana recognises the field as one we can use for temporal analyses. Otherwise the only timestamp field we&#8217;d have would contain the time at which the logs were processed and stored in Elasticsearch. Although not a problem in a realtime log analyses system, if you have old logs you want to parse you will need to define this separate timestamp field. As an additional sidenote, you&#8217;ll notice I use</p><pre class="crayon-plain-tag">tag_on_failure =&gt; []</pre><p>in most of my Grok stages. The default value is &#8220;_grokparsefailure&#8221;, which I don&#8217;t need in a production system. Custom failure and success tags are very helpful to debug your Logstash configs.</p>
<p>The next little section combines commit messages into a single line. The first event in the example log file is an example of such commit messages split over three lines.</p><pre class="crayon-plain-tag"># Combine commit events into single message
  multiline {
      pattern =&gt; "^\t(commit\{)"
      what =&gt; "previous"
    }</pre><p>Now we come to a major section for handling general INFO level messages.</p><pre class="crayon-plain-tag"># INFO level events treated differently than ERROR
  if "INFO" in [level] {
    grok {
      match =&gt; {
          "message" =&gt; ".+?; ((([a-zA-Z]+(\.|;|:))+) )+?\[%{WORD:collection}\].+?path=%{DATA:endpoint} params=\{%{DATA:params}\}.+?\{%{WORD:action}=\[%{DATA:docId}"
        }
        tag_on_failure =&gt; []  
    }
    if [params] {
      kv {
        field_split =&gt; "&amp;"
        source =&gt; "params"
      }
    } else {
      grok {
        match =&gt; {
          "message" =&gt; ".+?; ((([a-zA-Z]+(\.|;|:))+) )+?commits"  
        }
        tag_on_failure =&gt; [ "drop" ]
        add_field =&gt; {
          "action" =&gt; "commit"
        }
      }
      if "drop" in [tags] {
        drop {}
      }
    }
  }</pre><p>This filter will only run on INFO level messages, due to the conditional at its beginning. The first Grok stage matches log events similar to the one above. The key fields we extract are the Solr collection/core, the endpoint we hit e.g. update/extract, the parameters supplied by the HTTP request, what action e.g. add or delete and finally the document ID. If the Grok succeeded to extract a params field, we run the key value stage, splitting no ampersand to extract each HTTP parameter. This is how a resulting document&#8217;s extracted contents look like when stored in Elasticsearch:</p><pre class="crayon-plain-tag">{
  "level": "INFO",
  "LogTime": "2015-09-07 15:40:18.938",
  "collection": "sintef_main",
  "endpoint": "/update/extract",
  "params":     "literal.source=epifile&amp;literal.epi_file_title=A05100_Tass5+Trondheim.pdf&amp;literal.title=A05100_Tass5+Trondheim.pdf&amp;literal.id=epifile_211027&amp;literal.epifileid_s=211027&amp;literal.url=http://www.sintef.no/globalassets/upload/teknologi_samfunn/5036/a05100_tass5-trondheim.pdf&amp;stream.url=http://www.sintef.no/globalassets/upload/teknologi_samfunn/5036/a05100_tass5-trondheim.pdf&amp;literal.filesource_s=SiteFile",
  "action": "add",
  "docId": "epifile_211027",
  "version": "1511661994131849216",
  "literal.source": "epifile",
  "literal.epi_file_title": "A05100_Tass5+Trondheim.pdf",
  "literal.title": "A05100_Tass5+Trondheim.pdf",
  "literal.id": "epifile_211027",
  "literal.epifileid_s": "211027",
  "literal.url": "http://www.sintef.no/globalassets/upload/teknologi_samfunn/5036/a05100_tass5-trondheim.pdf",
  "stream.url": "http://www.sintef.no/globalassets/upload/teknologi_samfunn/5036/a05100_tass5-trondheim.pdf",
  "literal.filesource_s": "SiteFile"
}</pre><p>If the Grok did not extract a params field, I want to identify possible commit messages with the following Grok. If this one fails we tag messages with &#8220;drop&#8221;. Finally, any messages tagged with &#8220;drop&#8221; are dropped for the pipeline. I specifically created these Grok patterns to match indexing and commit messages as I already track queries at the middleware layer in our stack. If you want to track queries at the Solr level, simply use this pattern:</p><pre class="crayon-plain-tag">.+?; ((([a-zA-Z]+(\.|;|:))+) )+?\[%{WORD:collection}\].+?path=%{DATA:endpoint} params=\{%{DATA:params}\} hits=%{INT:hits} status=%{INT:status} QTime=%{INT:queryTime}</pre><p>The next section handles ERROR level messages:</p><pre class="crayon-plain-tag"># Error event implies stack track, which requires multiline parsing
  if "ERROR" in [level] {
    multiline {
      pattern =&gt; "^\s"
      what =&gt; "previous"
      add_tag =&gt; [ "multiline_pre" ]
    }
    multiline {
        pattern =&gt; "^Caused by"
        what =&gt; "previous"
        add_tag =&gt; [ "multiline_post" ]
    }
    if "multiline_post" in [tags] {
      grok {
        match =&gt; {
          "message" =&gt; ".+?; ((([a-zA-Z]+(\.|;|:))+) )+%{DATA:reason}(\n\t)((.+?Caused by: ((([a-zA-Z]+(\.|;|:))+) )+)%{DATA:reason}(\n\t))+"
        }
        tag_on_failure =&gt; []
      }
    }
  }</pre><p>Given a stack trace, there&#8217;s a few in the example log file, this stage first combines all the lines of the stack trace into a single message. It then extracts the first and the last causes. The assumption being the first message is the high level failure message and the last one the actual underlying cause.</p>
<p>Finally, I drop any empty lines and clean up temporary tags:</p><pre class="crayon-plain-tag"># Remove intermediate tags, and multiline added randomly by multiline stage
  mutate {
      remove_tag =&gt; [ "multiline_pre", "multiline_post", "multiline" ]
  }
  # Drop empty lines
  if [message] =~ /^\s*$/ {
    drop {}
  }</pre><p>To check you have succesfully processed your Solr logs, open up the Sense plugin and run this query:</p><pre class="crayon-plain-tag"># aggregate on level
GET solr-*/_search
{
  "query": {
    "match_all": {}
  },
  "size": 10,
  "aggs": {
    "action": {
      "terms": {
        "field": "level",
        "size": 10
      }
    }
  }
}</pre><p>You should get back all your processed log events along with an aggregation on event severity.</p>
<h2>Conclusion</h2>
<p>Solr logs contain a great deal of useful information. With the ELK stack you can extract, store, analyse and visualise this data. I hope I&#8217;ve given you some helpful tips on how to start doing so! If you run into any problems, please get in touch in the comments below.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.comperiosearch.com/blog/2015/09/21/solr-logstash-analysis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Solr: Indexing SQL databases made easier! &#8211; Part 2</title>
		<link>http://blog.comperiosearch.com/blog/2015/04/14/solr-indexing-index-sql-databases-made-easier-part-2/</link>
		<comments>http://blog.comperiosearch.com/blog/2015/04/14/solr-indexing-index-sql-databases-made-easier-part-2/#comments</comments>
		<pubDate>Tue, 14 Apr 2015 12:56:21 +0000</pubDate>
		<dc:creator><![CDATA[Seb Muller]]></dc:creator>
				<category><![CDATA[English]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Solr]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[solr5]]></category>

		<guid isPermaLink="false">http://blog.comperiosearch.com/?p=3477</guid>
		<description><![CDATA[Last summer I wrote a blog post about indexing a MySQL database into Apache Solr. I would like to now revisit the post to update it for use with Solr 5 and start diving into how to implement some basic search features such as Facets Spellcheck Phonetic search Query Completion Setting up our environment The [...]]]></description>
				<content:encoded><![CDATA[<p>Last summer I wrote a <a href="http://blog.comperiosearch.com/blog/2014/08/28/indexing-database-using-solr/">blog post</a> about indexing a MySQL database into <a href="http://lucene.apache.org/solr/">Apache Solr</a>. I would like to now revisit the post to update it for use with Solr 5 and start diving into how to implement some basic search features such as</p>
<ul>
<li>Facets</li>
<li>Spellcheck</li>
<li>Phonetic search</li>
<li>Query Completion</li>
</ul>
<h2>Setting up our environment</h2>
<p>The requirements remain the same as with the original blogpost:</p>
<ol>
<li>Java 1.7 or greater</li>
<li>A <a href="http://dev.mysql.com/downloads/mysql/">MySQL</a> database</li>
<li>A copy of the <a href="https://launchpad.net/test-db/employees-db-1/1.0.6/+download/employees_db-full-1.0.6.tar.bz2">sample employees database</a></li>
<li>The MySQL <a href="http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.32.tar.gz">jdbc driver</a></li>
</ol>
<p>We&#8217;ll now be using Solr 5, which runs a little differently from previous incarnations of Solr. Download <a href="http://www.apache.org/dyn/closer.cgi/lucene/solr/5.0.0">Solr</a> and extract it to a directory of your choice.Open a terminal and navigate to your Solr directory.<br />
Start Solr with the command <pre class="crayon-plain-tag">bin/solr start</pre> .<img class="alignright wp-image-3497 size-medium" src="http://blog.comperiosearch.com/wp-content/uploads/2022/04/Screen-Shot-2015-04-11-at-20.30.03-300x114.png" alt="Solr Status" width="300" height="114" /></p>
<p>To confirm Solr successfully started up, run <pre class="crayon-plain-tag">bin/solr status</pre></p>
<p>Unlike previously, we now need to create a Solr core for our employee data. To do so run this command <pre class="crayon-plain-tag">bin/solr create_core -c employees -d basic_configs</pre> . This will create a core named employees using Solr&#8217;s minimal configuration options. Try <pre class="crayon-plain-tag">bin/solr create_core -help</pre>  to see what else is possible.</p>
<ol>
<li>Open server/solr/employees/conf/solrconfig.xml in a text editor and add the following within the config tags:
<div id="file-dataimporthandler2-LC1" class="line">
<pre class="crayon-plain-tag">&lt;lib dir="../../../dist/" regex="solr-dataimporthandler-\d.*\.jar" /&gt;
 
&lt;requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"&gt;
&lt;lst name="defaults"&gt;
&lt;str name="config"&gt;db-data-config.xml&lt;/str&gt;
&lt;/lst&gt;
&lt;/requestHandler&gt;</pre>
</div>
</li>
<li>In the same directory, open schema.xml and add this this line:<br />
<pre class="crayon-plain-tag">&lt;dynamicField name="*_name" type="text_general" multiValued="false" indexed="true" stored="true" /&gt;</pre>
</li>
<li>Create a lib subdir in server/solr/employees and extract the MySQL jdbc driver jar into it.</li>
<li>Finally, restart the Solr server with the command <pre class="crayon-plain-tag">bin/solr restart</pre></li>
</ol>
<p>When started this way, Solr runs by default on port 8983. Use <pre class="crayon-plain-tag">bin/solr start -p portnumber</pre>  and replace portnumber with your preferred choice to start it on that one.</p>
<p>Navigate to <a href="http://localhost:8983/solr">http://localhost:8983/solr</a> and you should see the Solr admin GUI splash page. From here, use the Core Selector dropdown button to select our employee core and then click on the Dataimport option. Expanding the Configuration section should show an XML response with a stacktrace with a message along the lines of <pre class="crayon-plain-tag">Can't find resource 'db-data-config.xml' in classpath</pre> . This is normal as we haven&#8217;t actually created this file yet, which stores the configs for connecting to our target database.</p>
<p>We&#8217;ll come back to that file later but let&#8217;s make our demo database now. If you haven&#8217;t already downloaded the sample employees database and installed MySQL, now would be a good time!</p>
<h2>Setting up our database</h2>
<p>Please refer to the instructions in the same section in the <a href="http://blog.comperiosearch.com/blog/2014/08/28/indexing-database-using-solr/">original blog post</a>. The steps are still the same.</p>
<h2>Indexing our database</h2>
<p>Again, please refer to the instructions in the same section in the original blog post. The only difference is the Postman collection should be imported from <a href="https://www.getpostman.com/collections/f7634c89cd9851dd2c13"> this url</a> instead. The commands you can use alternatively have also changed and are now</p><pre class="crayon-plain-tag">Clear index: http://localhost:8983/solr/employees/update?stream.body=&lt;delete&gt;&lt;query&gt;*:*&lt;/query&gt;&lt;/delete&gt;&amp;commit=true
Retrieve all: http://localhost:8983/solr/employees/select?q=*:*&amp;omitHeader=true
Index db: http://localhost:8983/solr/employees/collection1/dataimport?command=full-import
Reload core: http://localhost:8983/solr/employees/admin/cores?action=RELOAD&amp;core=collection1
Georgi query: http://localhost:8983/solr/employees/select?q=georgi&amp;wt=json&amp;qf=first_name%20last_name&amp;defType=edismax
Facet query: http://localhost:8983/solr/employees/select?q=*:*&amp;wt=json&amp;facet=true&amp;facet.field=dept_s&amp;facet.field=title_s&amp;facet.mincount=1&amp;rows=0
Gorgi spellcheck: http://localhost:8983/solr/employees/select?q=gorgi&amp;wt=json&amp;qf=first_name&amp;defType=edismax
Georgi Phonetic: http://localhost:8983/solr/employees/select?q=georgi&amp;wt=json&amp;qf=first_name%20last_name%20phonetic&amp;defType=edismax</pre><p></p>
<h2>The next step</h2>
<p>We should now be back where we ended with the original blog post. So far we have successfully</p>
<ul>
<li>Setup a database with content</li>
<li>Indexed the database into our Solr index</li>
<li>Setup basic scheduled delta reindexing</li>
</ul>
<p>Let&#8217;s get started with the more interesting stuff!</p>
<h2>Facets</h2>
<p>Facets, also known as filters or navigators, allow a search user to refine and drill down through search results. Before we get started with them, we need to update our data import configuration. Replace the contents of our existing db-data-config.xml with:</p>
<div id="file-db-data-config2-LC1" class="line">
<pre class="crayon-plain-tag">select e.emp_no as 'id', e.birth_date,
(
select t.title
order by t.`from_date` desc
limit 1
) as 'title_s', e.first_name, e.last_name, e.gender as 'gender_s', d.`dept_name` as 'dept_s'
from employees e
join dept_emp de on de.`emp_no` = e.`emp_no`
join departments d on d.`dept_no` = de.`dept_no`
join titles t on t.`emp_no` = e.`emp_no`
group by e.`emp_no`
limit 1000;</pre><br />
To be able to facet, we need appropriate fields upon which to actually facet. Our new SQL retrieves additional fields such as employee titles and departments. Fields perfect for use as facets.</p>
</div>
<p><a href="http://blog.comperiosearch.com/wp-content/uploads/2015/04/Screen-Shot-2015-09-23-at-10.27.47.png"><img class="aligncenter size-medium wp-image-3520" src="http://blog.comperiosearch.com/wp-content/uploads/2015/04/Screen-Shot-2015-09-23-at-10.27.47.png" alt="Updated Employee SQL" width="300" height="166" /></a><br />
You&#8217;ll notice we map title, gender and dept_name to title_s, gender_s and dept_s respectively. This allows us to take advantage of an existing dynamic field mapping in Solr&#8217;s default basic config, *_s. A dynamic field allows us to assign all fields with a certain pre/suffix the same field type. In this case, given the field type <pre class="crayon-plain-tag">&lt;dynamicField name="*_s" type="string" indexed="true" stored="true" /&gt;</pre> , any fields ending with _s will be indexed and stored as basic strings. Solr will not tokenise them and modify their contents. This allows us to safely use them for faceting without worrying about department titles being split on white spaces for example.</p>
<ol>
<li>Clear the index and restart Solr.<a href="http://blog.comperiosearch.com/wp-content/uploads/2022/04/Screen-Shot-2015-04-13-at-17.06.22.png"><img class="alignright wp-image-3533 size-medium" src="http://blog.comperiosearch.com/wp-content/uploads/2022/04/Screen-Shot-2015-04-13-at-17.06.22-196x300.png" alt="Facet Query" width="196" height="300" /></a></li>
<li>Once Solr has restarted, reindex the database with<br />
our new SQL. Don&#8217;t be alarmed if this takes a bit longer<br />
than previously. It&#8217;s a bit more heavy weight and not<br />
very well optimised!</li>
<li>Once it&#8217;s done indexing, we can<br />
confirm it was successful by running the facet query via<br />
Postman or directly in our browser.</li>
<li>We should see two hits for the query &#8220;georgi&#8221; along with<br />
facets for their respective titles and department.</li>
</ol>
<p>&nbsp;</p>
<h2>The anatomy of a facet query</h2>
<p>Let&#8217;s take a closer look at the relevant request parameters of our facet query: <pre class="crayon-plain-tag">http://localhost:8983/solr/employees/select?q=georgi&amp;wt=json&amp;qf=first_name%20last_name&amp;defType=edismax&amp;omitHeader=true&amp;facet=true&amp;facet.field=dept_s&amp;facet.field=title_s&amp;facet.mincount=1</pre></p>
<ul>
<li>facet &#8211; Tells Solr to enable or prevent faceting. Accepted values include yes,on and true to enable, no, off and false to disable</li>
<li>facet.field &#8211; Which field we want to facet on, can be defined multiple times</li>
<li>facet.mincount &#8211; The minimum number of values for a particular facet value the query results includes for it to be included in the facet result object. Can be defined per facet field with this syntax f.fieldName.facet.mincount=1</li>
</ul>
<p>There are many other facet parameters. I recommend taking a look at the Solr wiki pages on <a href="https://wiki.apache.org/solr/SolrFacetingOverview">faceting</a> and other <a href="https://wiki.apache.org/solr/SimpleFacetParameters">possible parameters</a>.</p>
<h2>Spellcheck</h2>
<p>Analysing query logs and focusing on those queries that gave zero hits is a quick and easy way to see what can and should be done to improve your search solution. More often than not you will come across a great deal of spelling errors. Adding spellcheck to a search solution gives such great value for a tiny bit of effort. This fruit is so low hanging it should hit you in the face!</p>
<p>To enable spellcheck, we need to make some configuration changes.</p>
<ol>
<li>In our schema.xml, add these two lines after the *_name dynamic field type we added earlier:
<div class="line">
<pre class="crayon-plain-tag">&lt;copyField source="*_name" dest="spellcheck" /&gt;
&lt;field name="spellcheck" type="text_general" indexed="true" stored="true" multiValued="true" /&gt;</pre>
</div>
<p>A copyField checks for fields whose names match the pattern defined in source and copies their destinations to the dest field. In our case, we will copy content from first_name and last_name to spellcheck. We then define the spellcheck field as multiValued to handle its multiple sources.</li>
<li>Add the following to our solrconfig.xml:
<div id="file-spellcheck-LC1" class="line">
</p><pre class="crayon-plain-tag">&lt;searchComponent name="spellcheck" class="solr.SpellCheckComponent"&gt;
&lt;str name="queryAnalyzerFieldType"&gt;text_general&lt;/str&gt;
&lt;!-- a spellchecker built from a field of the main index --&gt;
&lt;lst name="spellchecker"&gt;
&lt;str name="name"&gt;default&lt;/str&gt;
&lt;str name="field"&gt;spellcheck&lt;/str&gt;
&lt;str name="classname"&gt;solr.DirectSolrSpellChecker&lt;/str&gt;
&lt;!-- the spellcheck distance measure used, the default is the internal levenshtein --&gt;
&lt;str name="distanceMeasure"&gt;internal&lt;/str&gt;
&lt;!-- minimum accuracy needed to be considered a valid spellcheck suggestion --&gt;
&lt;float name="accuracy"&gt;0.5&lt;/float&gt;
&lt;!-- the maximum #edits we consider when enumerating terms: can be 1 or 2 --&gt;
&lt;int name="maxEdits"&gt;2&lt;/int&gt;
&lt;!-- the minimum shared prefix when enumerating terms --&gt;
&lt;int name="minPrefix"&gt;1&lt;/int&gt;
&lt;!-- maximum number of inspections per result. --&gt;
&lt;int name="maxInspections"&gt;5&lt;/int&gt;
&lt;!-- minimum length of a query term to be considered for correction --&gt;
&lt;int name="minQueryLength"&gt;4&lt;/int&gt;
&lt;!-- maximum threshold of documents a query term can appear to be considered for correction --&gt;
&lt;float name="maxQueryFrequency"&gt;0.01&lt;/float&gt;
&lt;!-- uncomment this to require suggestions to occur in 1% of the documents
&lt;float name="thresholdTokenFrequency"&gt;.01&lt;/float&gt;
--&gt;
&lt;/lst&gt;
&lt;/searchComponent&gt;</pre><p>
</div>
<p>This will create a spellchecker component that uses the spellcheck field as its dictionary source. The spellcheck field contains content copied from both first and last name fields.</li>
<li>In the same file, look for the select requestHandler and update it to include the spellcheck component:
<div id="file-select-LC1" class="line">
</p><pre class="crayon-plain-tag">&lt;requestHandler name="/select" class="solr.SearchHandler"&gt;
&lt;!-- default values for query parameters can be specified, these
will be overridden by parameters in the request
--&gt;
&lt;lst name="defaults"&gt;
&lt;str name="echoParams"&gt;explicit&lt;/str&gt;
&lt;int name="rows"&gt;10&lt;/int&gt;
&lt;str name="spellcheck"&gt;on&lt;/str&gt;
&lt;str name="spellcheck.dictionary"&gt;default&lt;/str&gt;
&lt;/lst&gt;
&lt;!-- Add this to enable spellcheck --&gt;
&lt;arr name="last-components"&gt;
&lt;str&gt;spellcheck&lt;/str&gt;
&lt;/arr&gt;
&lt;/requestHandler&gt;</pre><p>
</div>
</li>
</ol>
<p>The defaults list in a requestHandler defines which default parameters to add to each request made using the chosen request handler. You could, for example, define which fields to query. In this case we&#8217;re enabling spellcheck and using the default dictionary as defined in our solrconfig.xml. All values in the defaults list can be overwritten per request. To include request parameters that cannot be overwritten, we would need to use an invariants list instead:</p><pre class="crayon-plain-tag">&lt;lst name="invariants"&gt;
&lt;str name="defType"&gt;edismax&lt;/str&gt;
&lt;/lst&gt;</pre><p>Both lists can be used simultaneously. When duplicate keys are present the values in the invariants list will take precedence.</p>
<p>Once we&#8217;ve made all our configuration changes, let&#8217;s restart Solr and reindex. To verify the changes worked, do a basic retrieve all query and check the resulting documents for the spellcheck field. Its contents should be the same as the document&#8217;s first_name and last_name fields.</p>
<p>Because we have enabled spellcheck by default,<a href="http://blog.comperiosearch.com/wp-content/uploads/2022/04/Screen-Shot-2015-04-14-at-15.20.45.png"><img class="alignright size-medium wp-image-3575" src="http://blog.comperiosearch.com/wp-content/uploads/2022/04/Screen-Shot-2015-04-14-at-15.20.45-174x300.png" alt="Gorgi" width="174" height="300" /></a> queries with possible suggestions will include contents in the spellcheck response object.</p>
<p>Try the Gorgi spellcheck query and experiment with different queries. To query the last_name field as well, change the qf parameter to <pre class="crayon-plain-tag">qf=first_name last_name</pre>.</p>
<p>The qf parameter defines which fields to use as the search domain.</p>
<p>When the spellcheck response object has content, you can easily use it to implement a basic &#8220;did you mean&#8221; feature. This will vastly improve your zero hit page.</p>
<h2>Phonetic Search</h2>
<p>Now that we have a basic spellcheck component in place, the next best feature that easily creates value in a people search system is phonetics. Solr ships with some <a href="https://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.PhoneticFilterFactory">basic</a> <a href="https://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.DoubleMetaphoneFilterFactor">phonetic</a> <a href="https://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.BeiderMorseFilterFactory">tokenisers</a>. The most commonly used out of the box phonetic tokeniser is the DoubleMetaphoneFilterFactory. It will suffice for most use cases. It does, however, have some weaknesses, which we will go into briefly in the next section.</p>
<p>We need to once again modify our schema.xml to take advantage of Solr&#8217;s phonetic capabilities. Add the following:</p><pre class="crayon-plain-tag">&lt;fieldType name="phonetic" class="solr.TextField" &gt;
 &lt;analyzer&gt;
 &lt;tokenizer class="solr.StandardTokenizerFactory"/&gt;
 &lt;filter class="solr.DoubleMetaphoneFilterFactory" inject="true" maxCodeLength="4"/&gt;
 &lt;/analyzer&gt;
 &lt;/fieldType&gt;

 &lt;copyField source="*_name" dest="phonetic" /&gt;
 &lt;field name="phonetic" type="phonetic" indexed="true" stored="false" multiValued="true" /&gt;</pre><p>Similar to spellcheck, we copy contents from the name fields into a phonetic field. Here we define a phonetic field, whose values will not be stored as we don&#8217;t need to return them in search results. It is, however, indexed so we can actually include it in the search domain. Finally, like spellcheck, it is multivalued to handle multiple potential sources. The reason we create an additional search field is so we can apply different weightings to exact matches and phonetic matches.</p>
<p>Restart Solr, clear the index and reindex.</p>
<p>Running the Georgi Phonetic search request should now returns hits based on exact and phonetic matches. To ensure that exact matches are ranked higher, we can add a query time boost to our query fields: <pre class="crayon-plain-tag">&amp;qf=first_name last_name phonetic^0.5</pre></p>
<p>Rather than apply boosts to fields we want to rank higher, it&#8217;s usually simpler to apply a punitive boost to fields we wish to rank lower. Replace the qf parameter in the Georgi Phonetic request and see how the first few results all have an exact match for georgi in the first_name field.</p>
<h2>Query Analysis</h2>
<p>As we look further down the result set, you will notice some strange matches. One employee, called Kirk Kalsbeek, is apparently a match for &#8220;georgi&#8221;. To understand why this is a match, we can use Solr&#8217;s analysis tool.<br />
<a href="http://blog.comperiosearch.com/wp-content/uploads/2022/04/Screen-Shot-2015-04-14-at-17.09.01.png"><img src="http://blog.comperiosearch.com/wp-content/uploads/2022/04/Screen-Shot-2015-04-14-at-17.09.01-300x247.png" alt="Solr Analysis" width="300" height="247" class="aligncenter size-medium wp-image-3585" /></a><br />
It allows use to define an indexed value, a query value and the field type to use and then demonstrate how each value is tokenised and whether or not the query would result in a match.</p>
<p>With the values Kirk Kalsbeek, georgi and phonetic respectively, the analysis tool shows us that Kirk gets tokenised to KRK by our phonetic field type. Georgi is also tokenised to KRK, which results in a match.</p>
<p>To create a better phonetic search solution, we would have to implement a custom phonetic tokeniser. I came across <a href="https://github.com/kvalle/norphoname"> an example</a>, which has helped me enormously in improving phonetic search for Norwegian names on a project.</p>
<h2>Conclusion</h2>
<p>We should now be able to </p>
<ul>
<li>Implement index field based spellcheck</li>
<li>Use basic faceting</li>
<li>Implement Solr&#8217;s out of the box phonetic capabilities</li>
</ul>
<p>Query completion I will leave for the next time. I promise you won&#8217;t have to wait as long between posts as last time :)</p>
<p>Let me know how you get on in the comments below!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.comperiosearch.com/blog/2015/04/14/solr-indexing-index-sql-databases-made-easier-part-2/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Solr As A Document Processing Pipeline</title>
		<link>http://blog.comperiosearch.com/blog/2015/01/16/custom-solr-update-request-processors/</link>
		<comments>http://blog.comperiosearch.com/blog/2015/01/16/custom-solr-update-request-processors/#comments</comments>
		<pubDate>Fri, 16 Jan 2015 10:40:48 +0000</pubDate>
		<dc:creator><![CDATA[Seb Muller]]></dc:creator>
				<category><![CDATA[English]]></category>
		<category><![CDATA[Solr]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[content enrichment]]></category>
		<category><![CDATA[Document Processing]]></category>
		<category><![CDATA[pipeline]]></category>
		<category><![CDATA[update request processor]]></category>

		<guid isPermaLink="false">http://blog.comperiosearch.com/?p=3050</guid>
		<description><![CDATA[Recently on a project I got an interesting request. Content owners wanted to enrich new documents submitted to the search index with content from documents already present in the index. We use Solr as the search backend for this particular customer so I started thinking about how to achieve this with Solr. A bit of [...]]]></description>
				<content:encoded><![CDATA[<p>Recently on a project I got an interesting request. Content owners wanted to enrich new documents submitted to the search index with content from documents already present in the index. We use Solr as the search backend for this particular customer so I started thinking about how to achieve this with Solr.</p>
<h2>A bit of Solr background</h2>
<p>Solr ships with all the tools and features necessary for an advanced search solution. These include the oft overlooked update request processors. They operate at the document level i.e. prior to individual field tokenisation and allow you to clean, modify and/or enrich incoming documents. Processing options include language identification, duplicate detection and HTML markup handling. Create a chain of them and you have a true document processing pipeline.</p>
<p>The Solr wiki includes a <a title="Update Request Processors" href="https://wiki.apache.org/solr/UpdateRequestProcessor#Full_list_of_UpdateRequestProcessor_Factories">brief entry </a> on the topic with an example of a custom processor that conditionally adds the field &#8220;cat&#8221; with value &#8220;popular&#8221;. The full list of UpdateRequestProcessor factories is available via the <a href="http://www.solr-start.com/info/update-request-processors/">Solr Start project</a>.</p>
<h2>Back to the initial request</h2>
<p>Certain incoming documents would contain a field, topicRef for example, with a reference to one or more documents already present in the index. The referenced documents could either contain a subsequent reference or content that we wanted to add to the incoming document. <a href="http://blog.comperiosearch.com/wp-content/uploads/2014/10/docProcess.png"><img class="size-medium wp-image-3054 alignright" src="http://blog.comperiosearch.com/wp-content/uploads/2014/10/docProcess-220x300.png" alt="document pipeline" width="220" height="300" /></a></p>
<p>I needed a mechanism to retrieve any referenced documents, traverse a tree of subsequently referenced documents if necessary, and then map the eventual leaf documents&#8217; specified content fields to additional new fields in the incoming document.</p>
<p>I created a recursive document enrichment processor to do just that!</p>
<p>Its settings allow for multiple potential field retrievals and mappings, local and foreign key field definitions and the option to retrieve content from a remote Solr index.</p>
<script src="https://gist.github.com/fcd5b45cd42a40b97daa.js?file=RecursiveMergeExistingDocFactory"></script>
<p>A minor drawback of the current iteration of the processor is a high reliance on the existence of referenced documents i.e. if the referenced documents are not already present in the index then the processor will skip over them. To ensure documents are fully enriched, especially if the referenced documents are included in the same indexing batch, reindexes of incoming documents is necessary unless explicitly defining the document indexing order.</p>
<p>In addition, when a referenced document is updated, content owners expect this to have an impact on the content of the parent document and therefore a user&#8217;s search experience. This is currently not the case as parent documents are unaware of their child documents beyond the indexing process.</p>
<p>I&#8217;m now thoroughly enjoying tackling these issues and working on the next iteration of this RecursiveMergeExistingDoc processor!</p>
<h2>Update &#8211; 06/02/15</h2>
<p>The source code is now available on <a href="https://github.com/sebnmuller/SolrDocumentEnricher">github</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.comperiosearch.com/blog/2015/01/16/custom-solr-update-request-processors/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Solr: Indexing SQL databases made easier!</title>
		<link>http://blog.comperiosearch.com/blog/2014/08/28/indexing-database-using-solr/</link>
		<comments>http://blog.comperiosearch.com/blog/2014/08/28/indexing-database-using-solr/#comments</comments>
		<pubDate>Thu, 28 Aug 2014 12:05:17 +0000</pubDate>
		<dc:creator><![CDATA[Seb Muller]]></dc:creator>
				<category><![CDATA[English]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Solr]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[indexing]]></category>
		<category><![CDATA[jdbc]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[people search]]></category>

		<guid isPermaLink="false">http://blog.comperiosearch.com/?p=2848</guid>
		<description><![CDATA[Update Part two is now available here! At the beginning of this year Christopher Vig wrote a great post about indexing an SQL database to the internet&#8217;s current search engine du jour, Elasticsearch. This first post in a two part series will show that Apache Solr is a robust and versatile alternative that makes indexing [...]]]></description>
				<content:encoded><![CDATA[<h3>Update</h3>
<p>Part two is now available <a href="http://blog.comperiosearch.com/blog/2015/04/14/solr-indexing-index-sql-databases-made-easier-part-2/">here!</a></p>
<hr />
<p>At the beginning of this year <a href="http://blog.comperiosearch.com/blog/author/cvig/">Christopher Vig</a> wrote a <a href="http://blog.comperiosearch.com/blog/2014/01/30/elasticsearch-indexing-sql-databases-the-easy-way/">great post </a>about indexing an SQL database to the internet&#8217;s current search engine du jour, <a href="http://www.elasticsearch.org/">Elasticsearch.</a> This first post in a two part series will show that <a href="http://lucene.apache.org/solr/">Apache Solr</a> is a robust and versatile alternative that makes indexing an SQL database just as easy. The second will go deeper into how to make leverage Solr&#8217;s features to create a great backend for a people search solution.</p>
<p>Solr ships with a configuration driven contrib called the <a href="http://wiki.apache.org/solr/DataImportHandler">DataImportHandler.</a> It provides a way to index structured data into Solr in both full and incremental delta imports. We will cover a simple use case of the tool i.e. indexing a database containing personnel data to form the basis of a people search solution. You can also easily extend the DataImportHandler tool via various <a href="http://wiki.apache.org/solr/DataImportHandler#Extending_the_tool_with_APIs">APIs</a> to pre-process data and handle more complex use cases.</p>
<p>For now, let&#8217;s stick with basic indexing of an SQL database.</p>
<h2>Setting up our environment</h2>
<p>Before we get started, there are a few requirements:</p>
<ol>
<li>Java 1.7 or greater</li>
<li>For this demo we&#8217;ll be using a <a href="http://dev.mysql.com/downloads/mysql/">MySQL</a> database</li>
<li>A copy of the <a href="https://launchpad.net/test-db/employees-db-1/1.0.6/+download/employees_db-full-1.0.6.tar.bz2">sample employees database</a></li>
<li>The MySQL <a href="http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.32.tar.gz">jdbc driver</a></li>
</ol>
<p>With that out of the way, let&#8217;s get Solr up and running and ready for database indexing:</p>
<ol>
<li>Download <a href="https://lucene.apache.org/solr/downloads.html">Solr</a> and extract it to a directory of your choice.</li>
<li>Open solr-4.9.0/example/solr/collection1/conf/solrconfig.xml in a text editor and add the following within the config tags:  <script src="https://gist.github.com/dd7cef212fd7f6a415b5.js?file=DataImportHandler"></script></li>
<li>In the same directory, open schema.xml and add this this line   <script src="https://gist.github.com/5bbc8c6e1a5b617b5d16.js?file=names"></script></li>
<li>Create a lib subdir in solr-4.9.0/solr/collection1/ and extract the MySQL jdbc driver jar into it. It&#8217;s the file called mysql-connector-java-{version}-bin.jar</li>
<li>To start Solr, open a terminal and navigate to the example subdir in your extracted Solr directory and run <code>java -jar start.jar</code></li>
</ol>
<p>When started this way, Solr runs by default on port 8983. If you need to change this, edit solr-4.9.0/example/etc/jetty.xml and restart Solr.</p>
<p>Navigate to <a href="http://localhost:8983/solr">http://localhost:8983/solr</a> and you should see the Solr admin GUI splash page. From here, use the Core Selector dropdown button to select the default core and then click on the Dataimport option. Expanding the Configuration section should show an XML response with a stacktrace with a message along the lines of <code>Can't find resource 'db-data-config.xml' in classpath</code>. This is normal as we haven&#8217;t actually created this file yet, which stores the configs for connecting to our target database.</p>
<p>We&#8217;ll come back to that file later but let&#8217;s make our demo database now. If you haven&#8217;t already downloaded the sample employees database and installed MySQL, now would be a good time!</p>
<h2>Setting up our database</h2>
<p>Assuming your MySQL server is installed <a href="http://blog.comperiosearch.com/wp-content/uploads/2014/12/createdatabase.png"><img class="alignright size-full wp-image-2900" src="http://blog.comperiosearch.com/wp-content/uploads/2014/12/createdatabase-300x226.png" alt="Prepare indexing database" width="300" height="226" /></a>and running, access the MySQL terminal and create the empty employees database: <code>create database employees;</code></p>
<p>Exit the MySQL terminal and import the employees.sql into your empty database, ensuring that you carry out the following command from the same directory as the employees.sql file itself: <code>mysql -u root -p employees &lt; employees.sql</code></p>
<p>You can test this was successful by logging <a href="http://blog.comperiosearch.com/wp-content/uploads/2014/08/testdatabase.png"><img class="alignright size-medium wp-image-2900" src="http://blog.comperiosearch.com/wp-content/uploads/2014/08/testdatabase-276x300.png" alt="Verify indexing database" width="276" height="300" /></a>into the MySql server and querying the database, as shown here on the right.</p>
<p>Having successfully created and populated your employee database, we can now create that missing db-data-config.xml file.</p>
<h2>Indexing our database</h2>
<p>In your Solr conf directory, which contains the schema.xml and solrconfig.xml we previously modified, create a new file called db-data-config.xml.</p>
<p>Its contents should look like the example below. Make sure to replace the user and password values with yours and feel free to modify or remove the limit parameter. There&#8217;s approximately 30&#8217;000 entries in the employees table in total <script src="https://gist.github.com/03935f1384e150504363.js?file=db-data-config"></script></p>
<p>We&#8217;re now going to make use of Solr&#8217;s REST-like HTTP API with a couple of commands worth saving. I prefer to use the <a href="https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm">Postman app</a> on Chrome and have created a public collection of HTTP requests, which you can import into Postman&#8217;s Collections view using this url: <a href="https://www.getpostman.com/collections/9e95b8130556209ed643">https://www.getpostman.com/collections/9e95b8130556209ed643</a></p>
<p>For those of you not using Chrome, here are the commands you will need:<script src="https://gist.github.com/05a2a1dd01a6c5a4517b.js?file=solr-http"></script> First let&#8217;s reload the core so that Solr is <a href="http://blog.comperiosearch.com/wp-content/uploads/2014/08/reloadcore.png"><img class="alignright size-medium wp-image-2921" src="http://blog.comperiosearch.com/wp-content/uploads/2014/08/reloadcore-300x181.png" alt="Reload Solr core" width="300" height="181" /></a><br />
aware of the new db-data-config.xml file we have created.<br />
Next, we index our database with the <a href="http://blog.comperiosearch.com/wp-content/uploads/2014/08/indexdb.png"><img class="alignright size-medium wp-image-2923" src="http://blog.comperiosearch.com/wp-content/uploads/2014/08/indexdb-300x181.png" alt="Index database to Solr" width="300" height="181" /></a>HTTP request or from within the Solr Admin GUI on the DataImport page.</p>
<p>Here we have carried out a full index of our database using the full-import command parameter. To only retrieve changes since the last import, we would use delta-import instead.</p>
<p>We can confirm that our database import was successful by querying our index with the &#8220;Retrieve all&#8221; and &#8220;Georgi query&#8221; requests.</p>
<p>Finally, to schedule reindexing you can use a simple cronjob. This one, for example, will run everyday at 23:00 and retrieve all changes since the previous indexing operation:<script src="https://gist.github.com/47f6df5a306e4cd51617.js?file=delta"></script></p>
<h2>Conclusion</h2>
<p>So far we have successfully</p>
<ul>
<li>Setup a database with content</li>
<li>Indexed the database into our Solr index</li>
<li>Setup basic scheduled delta reindexing</li>
</ul>
<p>In the next part of this two part series we will look at how to process our indexed data. Specifically, with a view to making a good people search solution. We will implement several features such as phonetic search, spellcheck and basic query completion. In the meantime, let&#8217;s carry on the conversation in the comments below!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.comperiosearch.com/blog/2014/08/28/indexing-database-using-solr/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
