<?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>/dev/tty &#187; Ward Bekker</title>
	<atom:link href="http://blog.tty.nl/author/ward/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.tty.nl</link>
	<description>Notes on Web Development, Computer Programming, and Software Engineering</description>
	<lastBuildDate>Thu, 29 Dec 2011 10:59:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Quest for the perfect Erlang development environment</title>
		<link>http://blog.tty.nl/2011/10/20/quest-for-the-perfect-erlang-development-environment/</link>
		<comments>http://blog.tty.nl/2011/10/20/quest-for-the-perfect-erlang-development-environment/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 10:21:35 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Erlang]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=509</guid>
		<description><![CDATA[Erlang is great, and there is a lot of dev tooling available. Unfortunately these best practices are not easy to find for Erlang newbies like me. So I&#8217;ll start writing them down here and grow the list as I&#8217;m moving &#8230; <a href="http://blog.tty.nl/2011/10/20/quest-for-the-perfect-erlang-development-environment/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Erlang is great, and there is a lot of dev tooling available. Unfortunately these best practices are not easy to find for Erlang newbies like me. So I&#8217;ll start writing them down here and grow the list as I&#8217;m moving up the <a href="http://en.wikipedia.org/wiki/Dreyfus_model_of_skill_acquisition">Dreyfus model</a></p>
<p><strong>Get command history for `erl`, the Erlang shell</strong></p>
<ol>
<li>Install <a href="http://freshmeat.net/projects/rlwrap/">rlwrap</a>. On my Mac using <a href="http://mxcl.github.com/homebrew/">Homebrew</a>:
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">brew install rlwrap</div></div>
</li>
<li>Add the alias
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">alias erl='rlwrap -a dummy erl'</div></div>
<p>in your Bash profile. On my Mac it&#8217;s located here:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">~/.profile</div></div>
<p>. Reload your profile like this:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">bash$ source ~/.profile</div></div>
</li>
</ol>
<p><strong>Automatic reloading of re-compiled modules</strong></p>
<ol>
<li>Grab <a href="https://github.com/mochi/mochiweb">Mochiweb&#8217;s</a>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">reload.erl</div></div>
<p>from <a href="https://github.com/mochi/mochiweb/blob/master/src/reloader.erl">here</a>, compile it and put the beam file here:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">~/bin/reloader.beam</div></div>
</li>
<li>Create or edit
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">~/.erlang</div></div>
<p>and add the line</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">code:load_abs(&quot;[YOUR_HOME_DIR_PLZ_REPLACE]/bin/reloader&quot;)</div></div>
<p>.</li>
<li>From now on, when you have a module loaded in the Erlang shell and re-compile it outside your shell, the new version will be reloaded automatically</li>
</ol>
<p><strong>Some nice utility functions for your Erlang shell</strong></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">** user extended commands **<br />
dbgtc(File) &nbsp; -- use dbg:trace_client() to read data from File<br />
dbgon(M) &nbsp; &nbsp; &nbsp;-- enable dbg tracer on all funs in module M<br />
dbgon(M,Fun) &nbsp;-- enable dbg tracer for module M and function F<br />
dbgon(M,File) -- enable dbg tracer for module M and log to File<br />
dbgadd(M) &nbsp; &nbsp; -- enable call tracer for module M<br />
dbgadd(M,F) &nbsp; -- enable call tracer for function M:F<br />
dbgdel(M) &nbsp; &nbsp; -- disable call tracer for module M<br />
dbgdel(M,F) &nbsp; -- disable call tracer for function M:F<br />
dbgoff() &nbsp; &nbsp; &nbsp;-- disable dbg tracer (calls dbg:stop/0)<br />
l() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -- load all changed modules<br />
la() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-- load all modules<br />
mm() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-- list modified modules</div></div>
<p>These commands are added by:</p>
<ol>
<li>Compiling <a href="https://gist.github.com/1300812">user_default.erl</a> and move the beam file to
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">~/bin/user_default.beam</div></div>
</li>
<li>Create or edit
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">~/.erlang</div></div>
<p>and add the line</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">code:load_abs(&quot;[YOUR_HOME_DIR_PLZ_REPLACE]/bin/user_default&quot;)</div></div>
<p>.</li>
<li>Feel free to add your own shortcuts to your
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">user_default.erl</div></div>
<p>.</li>
</ol>
<p>There are multiple versions of</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">user_default.erl</div></div>
<p>floating around on the interwebs. <a href="http://www.google.com/codesearch#search/&#038;q=lang:%5Eerlang$%20file:user_default.erl&#038;type=cs">So pick the one that feels right</a>.</p>
<p><strong>Practical Erlang testing techniques</strong></p>
<p>Watch the <a href="http://etrepum.github.com/erl_testing_2011/"> Practical Erlang testing techniques presentation</a> from <a href="http://twitter.com/#!/etrepum">Mr. Bob Ippolito</a> for a quick rundown of useful testing libs. </p>
<p>Thanks to <a href="http://twitter.com/#!/andrzejsliwa" title="@andrzejsliwa">@andrzejsliwa</a> for the tips! Please add your tips to the comments and I&#8217;ll update the post.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2011/10/20/quest-for-the-perfect-erlang-development-environment/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Disabling resuming of apps in OSX Lion</title>
		<link>http://blog.tty.nl/2011/10/20/disabling-resuming-of-apps-in-osx-lion/</link>
		<comments>http://blog.tty.nl/2011/10/20/disabling-resuming-of-apps-in-osx-lion/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 07:05:13 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=506</guid>
		<description><![CDATA[The new application resume feature of OSX Lion annoys me big time. To disable it, you need to do the following steps: 1) Disable the `Restore windows when quitting and re-opening apps` checkbox in the General Preference window. 2) Clean &#8230; <a href="http://blog.tty.nl/2011/10/20/disabling-resuming-of-apps-in-osx-lion/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The new application resume feature of OSX Lion annoys me big time. To disable it, you need to do the following steps:</p>
<p>1) Disable the `Restore windows when quitting and re-opening apps` checkbox in the General Preference window.</p>
<div class="thumbnail"><a href="https://skitch.com/wardbekker/gnrua/general"><img src="https://img.skitch.com/20111020-xgumks38ei4i843jpchj73hu5e.preview.jpg" alt="General" /></a></div>
<p>2) Clean out and write-protect the Resume database for the command line:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">rm -rf ~/Library/Saved\ Application\ State/*<br />
chmod -w ~/Library/Saved\ Application\ State/</div></div>
<p>Thanks to <a href="http://twitter.com/#!/andrzejsliwa" title="@andrzejsliwa">@andrzejsliwa</a> for the tip!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2011/10/20/disabling-resuming-of-apps-in-osx-lion/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Basic Full Text Search Server in Erlang</title>
		<link>http://blog.tty.nl/2011/10/07/a-basic-full-text-search-server-in-erlang/</link>
		<comments>http://blog.tty.nl/2011/10/07/a-basic-full-text-search-server-in-erlang/#comments</comments>
		<pubDate>Fri, 07 Oct 2011 05:44:25 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Erlang]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=467</guid>
		<description><![CDATA[This post explains how to build a basic full text search server in Erlang. The server has the following features: indexing stemming ranking faceting asynchronous search results web frontend using websockets Familiarity with the OTP design principles is recommended. The &#8230; <a href="http://blog.tty.nl/2011/10/07/a-basic-full-text-search-server-in-erlang/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<code><p>This post explains how to build a basic full text search server in Erlang. The server has the following features:</p>

<ul>
<li>indexing</li>
<li>stemming</li>
<li>ranking</li>
<li>faceting</li>
<li>asynchronous search results</li>
<li>web frontend using websockets</li>
</ul>

<p>Familiarity with the <a href="http://www.erlang.org/doc/design_principles/des_princ.html">OTP design principles</a> is recommended.</p>

<p>The sample application (build with help from my colleague Michel Rijnders <code>&lt;mies@tty.nl&gt;</code>) uses the <a href="http://blog.stackoverflow.com/category/cc-wiki-dump/">Creative Commons Data Dump from StackExchange</a> as demo data.</p>

<p>We cover the following subjects:</p>

<ul>
<li><a href="#sample_application">running the sample application</a></li>
<li><a href="#otp_tree">OTP supervision tree</a></li>
<li><a href="#data_import">importing demo data</a></li>
<li><a href="#indexing">indexing</a></li>
<li><a href="#stemming">stemming</a></li>
<li><a href="#faceting">faceting</a></li>
<li><a href="#query_and_ranking">querying and relevance ranking</a></li>
<li><a href="#display_search">displaying search results</a></li>
<li><a href="#improvements">improvements</a></li>
</ul>

<p><a name="sample_application" ></a></p>

<h2>Running the Sample Application</h2>

<p>Clone the source from GitHub:</p>

<pre><code> git clone git://github.com/tty/async_search.git
</code></pre>

<p>And start the application:</p>

<pre><code>$ rebar get-deps compile &amp;&amp; erl -pa `pwd`/ebin `pwd`/deps/*/ebin +P 134217727
Eshell&gt; application:start(async).
Eshell&gt; stackoverflow_importer_ser:import().
</code></pre>

<p>Visit <a href="http://localhost:3000">http://localhost:3000</a>, you should see the following page:</p>

<p><img src="https://img.skitch.com/20110909-f3i2aiuby9ht1sjh1j5yt42f7x.jpg" alt="http://localhost:3000/" /></p>

<p>Sample ranked search output for the query <code>erlang armstrong</code>:</p>

<div class="thumbnail"><a href="https://skitch.com/wardbekker/fas4h/http-localhost-3000"><img src="https://img.skitch.com/20110909-efekgnjk3hwuuhpea5dq5gi48m.preview.jpg" alt="http://localhost:3000/" /></a></div>

<p>Sample tags facets output for the query <code>java</code>:</p>

<div class="thumbnail"><a href="https://skitch.com/wardbekker/fas51/http-localhost-3000"><img src="https://img.skitch.com/20110909-ed4e8kcaenbn4bkh6ddt342ig2.preview.jpg" alt="http://localhost:3000/" /></a></div>

<p><a name="otp_tree" ></a></p>

<h2>OTP Supervision Tree</h2>

<div class="thumbnail"><a href="https://skitch.com/wardbekker/f2rki/supervisor-tree"><img style="max-width:638px" src="https://img.skitch.com/20110911-kcd3i2gexishp7e92m3mcxpurn.medium.jpg" alt="supervisor tree" /></a></div>

<p>Looking at the OTP application supervision tree is a good way to understand the architecture of an OTP application.</p>

<p>The application supervisor <code>async_sup</code> starts up the following supervisors:</p>

<ul>
<li><code>keyword_sup</code>. A <code>keyword_ser</code> process is created for every unique word in the StackExchange posts. This <code>keyword_ser</code> is linked to the <code>keyword_sup</code> supervisor (a <code>simple_one_for_one</code> supervisor). The <code>keyword_ser</code> child process maintains a list of document positions of a keyword  (an <a href="http://en.wikipedia.org/wiki/Inverted_index">inverted index</a>).</li>
<li><code>facet_sup</code>. A <code>keyword_ser</code> process is also created for every unique facet category in the StackExchange posts. This <code>keyword_ser</code> process is linked to the <code>facet_sup</code> supervisor (a <code>simple_one_for_one</code> supervisor as well). The <code>keyword_ser</code> child process maintains a list of facet values with the IDs of the documents the facets appear in.</li>
</ul>

<p>The application supervisor also start the following <code>gen_server</code> singleton processes:</p>

<ul>
<li><code>stackoverflow_importer_ser</code>. This server imports the demo Stack Overflow data.</li>
<li><code>document_ser</code>. This server holds a copy of all documents, so it can return the original title and body of matching Stack Overflow posts in the results.</li>
<li><code>query_ser</code>. This server's task is to run the actual query and return results.</li>
<li><code>websocket_ser</code>. This server provides a HTTP frontend for the search engine.</li>
</ul>

<p>No attention is given to fault tolerance (apart from the basic restart strategies), thus parts of the search index are lost if a <code>keyword_ser</code> process terminates.</p>

<p><a name="data_import" ></a></p>

<h2>Demo Data Import</h2>

<p>The StackExchange data is provided as XML. Since some of the documents are quite large, it's not recommended to load the full XML documents in memory. The solution is to use a <a href="http://nl.wikipedia.org/wiki/Simple_API_for_XML">SAX parser</a> which treats a XML file as a stream, and triggers events when new elements are discovered. The search server uses the excellent SAX parser from the <a href="http://erlsom.sourceforge.net">Erlsom</a> library by Willem de Jong.</p>

<p>In the example below <code>erlsom:parse_sax</code> reads the XML file from <code>FilePath</code> and calls the function <code>sax_event</code> if an XML element is found.</p>

<script src="https://gist.github.com/1203600.js?file=stackoverflow_importer_ser.erl"></script>

<p>When the element is a <code>row</code> element (i.e. a post element), attributes like <code>Id</code>, <code>Title</code> and <code>Body</code> are stored in a dictionary. For every post a copy of all the attributes in <code>document_ser</code> is saved. This is used for returning the actual posts for a query match.  After that the <code>add_attribute_tokens</code> function is called:</p>

<script src="https://gist.github.com/1203611.js?file=stackoverflow_import_ser.erl"></script>

<p>The <code>add_attribute_tokens</code> function does two things. It calls <code>add_facet</code> (discussed later) and it creates a list of tuples with all the words and their position in the document. This process is called <a href="http://en.wikipedia.org/wiki/Tokenization">tokenization</a>. Each token/position tuple is then submitted to the <code>add_keyword_position</code> function of the <code>keyword_ser</code> for indexing.</p>

<script src="https://gist.github.com/1205618.js?file=stackoverflow_import_ser.erl"></script>

<p><a name="indexing"></a></p>

<h2>Indexing</h2>

<p>Indexing of the tuples, or keywords, is handled by the <code>keyword_ser</code>. For every unique word a <code>keyword_ser</code> process is started if not already present. The state of a <code>keyword_ser</code> process is a dictionary with the document ID as key and a list of positions as value.  The document ID corresponds to the ID of the Stack Overflow post.</p>

<script src="https://gist.github.com/1205643.js?file=keyword_ser.erl"></script>

<p>The <code>keyword_server_name</code> function generates a unique name under which the <code>keyword_ser</code> process is registered, so the module can check if a keyword already has a process or a new process needs to be created.</p>

<script src="https://gist.github.com/1205645.js?file=keyword_ser.erl"></script>

<p><a name="stemming" ></a></p>

<h2>Stemming</h2>

<p><a href="http://en.wikipedia.org/wiki/Stemming">Stemming</a> is the process for reducing inflected words to their base form. <code>Computing</code> and <code>computer</code> both are stemmed to <code>comput</code>. So when a user searches on <code>computing</code>, it also matches text that contains <code>computer</code>.  This makes it possible to return results that are relevant, but do not exactly match the query.</p>

<p>In our sample application all keywords are stemmed using the popular <a href="http://tartarus.org/~martin/PorterStemmer/">Porter Algorithm</a>. The <a href="http://tartarus.org/~martin/PorterStemmer/porter.erl">Erlang implementation</a> by Alden Dima is used in the application.</p>

<script src="https://gist.github.com/1205681.js?file=keyword_ser.erl"></script>

<p><code>erlang:phash2</code> is used to transform the stemmed name to a hash, to make sure the registered process name is valid.</p>

<p><a name="faceting" ></p>

<h2>Faceting</h2>

<p><a href="http://en.wikipedia.org/wiki/Faceted_search">Faceted search</a> is an important navigation feature for search engines. A user can drill down the search results by filtering on pre-defined attributes, like in this example of a digital camera search on CNET:</p>

<p><img src="http://weblogs.asp.net/blogs/drnetjes/CNET_faceted_search.jpg" alt="Faceted search example" title="" /></p>

<p>As mentioned above, the data import the function <code>add_attribute_tokens</code> also calls the <code>add_facet</code> function. Using pattern matching the <code>Tags</code> and the <code>Creationdate</code> attributes are selected for faceting. <code>Tags</code> is a so called multivalue facet, as a Stack Overflow post can have one or more tags assigned. For every tag and creation date the <code>facet_ser:add_facet_value</code> function is called.</p>

<script src="https://gist.github.com/1205699.js?file=stackoverflow_importer_ser.erl"></script>

<p><code>facet_ser</code> works very similar to <code>keyword_ser</code>. For every facet category, <code>Tag</code> or <code>Creationdate</code> in our case, a <code>facet_ser</code> processes is started. The state of a <code>facet_ser</code> is a dictionary with the <code>Tag</code> or <code>Creationdate</code> values as key and their document IDs as dictionary values.</p>

<p><a name="query_and_ranking" ></a></p>

<h2>Querying and Relevance Ranking</h2>

<p>In previous sections is shown:</p>

<ul>
<li>how the XML demo data is parsed.</li>
<li>how this data is stemmed and indexed by creating a <code>keyword_ser</code> process for every unique keyword.</li>
<li>how this data is indexed for faceted search by creating a <code>facet_ser</code> process for every facet category.</li>
</ul>

<p>With the function <code>stackoverflow_importer_ser:import()</code> these steps are executed, and your Erlang node is now ready for querying. So how does that work?</p>

<h3>Querying</h3>

<p>Querying is handled by passing the user's query terms to the function <code>do_async_query</code> of the singleton <code>query_ser</code> server.  When calling this function you need to specify the module, function and optional reference attribute which will be called when query results are available.</p>

<script src="https://gist.github.com/1205827.js?file=query_ser.erl"></script>

<p>In the <code>handle_cast</code> the following steps are executed:</p>

<ul>
<li><code>keyword_ser:do_query</code> return all document ids that contain one or more of the user's query terms, including the relevance ranking score, which will be discussed below.</li>
<li>All original documents are stored during indexing in a <code>document_ser</code> process. All matching documents are collected.</li>
<li>The callback function is invoked with the matching documents and their ranking scores as arguments.</li>
<li>Facet results are retrieved for any <code>FacetCategories</code> that are specified by calling <code>facet_ser:get_facets</code>.</li>
<li>And the callback function is invoked a second time with the facet results as arguments.</li>
</ul>

<script src="https://gist.github.com/1205850.js?file=query_ser.erl"></script>

<h3>Relevance Ranking</h3>

<p><a href="http://en.wikipedia.org/wiki/Relevance_(information_retrieval)">Relevance</a> in this context denotes how well a retrieved document matches the user's search query. Most fulltext search-engines use the <a href="http://en.wikipedia.org/wiki/Okapi_BM25">BM25</a> algorithm to determine the ranking score of each document, so let's use that too.</p>

<p>BM25 calculates a ranking score based on the query term frequency in each documents.</p>

<p>See the <a href="https://github.com/tty/async_search/blob/master/src/async_bm25.erl">async_bm25.erl</a> for the implementation.</p>

<p><a name="display_search" ></a></p>

<h2>Displaying the Search Results</h2>

<p>As discussed, the <code>query_ser:do_async_query</code> can be called to query our full-text search engine. To allow users to send queries and see the result the <code>websocket_ser</code> module is created. This singleton <code>gen_server</code>starts up a <a href="https://github.com/ostinelli/misultin">Misultin HTTP server</a> on Port 3000. If you browse to <a href="http://localhost:3000">http://localhost:3000</a> you will see a search box. Communication with the search engine is done through websockets.</p>

<p>So, when a user posts a query, this message is received by the <code>websockets_ser:handle_websocket</code> receive block.  The <code>query_ser:do_async_query</code> function is called and query results are expected on <code>websockets_ser:query_results</code> function.</p>

<script src="https://gist.github.com/1206002.js?file=websocket_ser.erl"></script>

<p>The <code>query_results</code> function formats the results as HTML and sends this through the websocket. When received, the HTML is appended to the user's page.</p>

<script src="https://gist.github.com/1206014.js?file=websocket_ser.erl"></script>

<p>A similar process is executed when the facet results are received:</p>

<script src="https://gist.github.com/1206022.js?file=websocket_ser.erl"></script>

<p><a name="improvements"></a></p>

<h2>Improvements</h2>

<p>Some obvious features that are lacking from this sample application:</p>

<ul>
<li>The author of this post is an Erlang newbie. Corrections/suggestions to the code are most welcome. You can send them to <code>&lt;ward@tty.nl&gt;</code></li>
<li>Pretty much no attention is given to performance / memory usage.</li>
<li>Fault tolerence for the index data. When a server containing index state dies, it will not be revived.</li>
<li>Tuple structures passed between modules are not specified. Would be nice to use record syntax for it.</li>
<li>No unit/quickcheck/common test added.</li>
<li>No function/type specifications.</li>
<li>etc..</li>
</ul>

<p>So, that why it's called a <em>sample</em> application ;-)</p>

<script type="text/javascript">var _gaq = _gaq || [];_gaq.push(['_setAccount', 'UA-26164383-1']);_gaq.push(['_trackPageview']);
  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
</script></code>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2011/10/07/a-basic-full-text-search-server-in-erlang/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Erlang Factory Lite Amsterdam Talks Announced</title>
		<link>http://blog.tty.nl/2011/10/03/erlang-factory-lite-amsterdam-speakers-announced/</link>
		<comments>http://blog.tty.nl/2011/10/03/erlang-factory-lite-amsterdam-speakers-announced/#comments</comments>
		<pubDate>Mon, 03 Oct 2011 12:27:06 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Erlang]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=481</guid>
		<description><![CDATA[See http://www.erlang-factory.com/conference/amsterdam for more details and free registration Travis CI – Distributed, Continuous Integration for the open source community. By Ward Bekker / TTY Internet Solutions &#8211; Travis CI is a new continuous integration service for the open source community. &#8230; <a href="http://blog.tty.nl/2011/10/03/erlang-factory-lite-amsterdam-speakers-announced/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>See <a href="http://www.erlang-factory.com/conference/amsterdam">http://www.erlang-factory.com/conference/amsterdam</a> for more details and free registration</p>
<p><strong>Travis CI – Distributed, Continuous Integration for the open source community.</strong></p>
<p><em>By Ward Bekker / TTY Internet Solutions</em> &#8211; Travis CI is a new continuous integration service for the open source community. It started out with a Ruby focus and became an instant success. Recently Erlang support was added. A few well known projects, like eTorrent, Mochiweb, Meck and Elixir, already started using it. In this presentation you will learn how the system works, the vision behind it, the upcoming features the team is working on and how to add your own Erlang projects.</p>
<p><strong>The Erlang trace facility</strong></p>
<p><em>By Jeroen Koops</em> – In this talk, I’ll show how to use Erlang’s low-level trace facility, and the higher-level dbg module that is built on top of it. Finally, I’ll demonstrate how to build a simple tool using the primitives provided by the trace-facility.</p>
<p><strong>Let’s jabber about ejabberd</strong></p>
<p><em>By Ahmed Omar / Nimbuzz</em> &#8211; Just a quick jabber about ejabberd</p>
<p><strong>Zotonic, the Erlang web framework, at MaxClass<br />
</strong><br />
<em>By Marc Worrell / MaxClass</em> &#8211; Zotonic is both an easy to use content management system and a powerful web framework. It’s built on some of the best pieces of Erlang open source software, by experienced web developers. Zotonic comes with an incredible speed out-of-the-box, an extensible infrastructure and most of all, a friendly community. In the first part of this talk, we summarize the history and development of Zotonic, and give a short introduction to the data model and the architecture.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2011/10/03/erlang-factory-lite-amsterdam-speakers-announced/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Travis now available in the Erlang flavor</title>
		<link>http://blog.tty.nl/2011/08/19/travis-now-available-in-the-erlang-flavour/</link>
		<comments>http://blog.tty.nl/2011/08/19/travis-now-available-in-the-erlang-flavour/#comments</comments>
		<pubDate>Fri, 19 Aug 2011 07:49:48 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Erlang]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=458</guid>
		<description><![CDATA[Travis, the very popular and open distributed build system for the Ruby community, has diversified. It now also features first class Erlang support. It came together with help from former colleague Josh Kaldermis and the other Travis devs. Thx guys! &#8230; <a href="http://blog.tty.nl/2011/08/19/travis-now-available-in-the-erlang-flavour/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://travis-ci.org/">Travis</a>, the very popular and open distributed build system for the Ruby community, has diversified. It now also features first class Erlang support. It came together with help from former colleague <a href="http://twitter.com/#!/joshkalderimis">Josh Kaldermis</a> and the other Travis devs. Thx guys! Also many thanks to <a href="http://www.tty.nl/">TTY Internet Solutions</a> for providing a server for hosting the <a href="http://about.travis-ci.org/docs/dev/worker/">workers</a>.</p>
<p>Currently we provide Erlang/OTP releases R14B01, R14B02 and R14B03. Older versions will be added in the near future. Builds are managed with the excellent <a href="https://github.com/basho/rebar">Rebar</a> tool from the <a href="http://www.basho.com/">Basho</a> folks.</p>
<h2>Projects</h2>
<p>A selection of projects that were added to <a href="http://travis-ci.org/">Travis</a> at the time of writing:</p>
<ul>
<li><a href="http://travis-ci.org/#!/jlouis/etorrent">jlouis/eTorrent</a></li>
<li><a href="http://travis-ci.org/#!/mochi/mochiweb">mochi/Mochiweb</a></li>
<li><a href="http://travis-ci.org/#!/spawngrid/proper_stdlib">spawngrid/Proper_stdlib</a></li>
<li><a href="http://travis-ci.org/#!/seancribbs/neotoma">seancribbs/Neotoma</a></li>
<li><a href="http://travis-ci.org/#!/dreid/gen_bunny">dreid/Gen_Bunny</a></li>
</ul>
<p>So, why not add <a href="http://about.travis-ci.org/">your Erlang project</a> now?</p>
<h2>The near future</h2>
<p>Currently only <a href="http://www.erlang.org/doc/man/eunit.html">eunit</a> tests are run. We are going to add support for:</p>
<ul>
<li><a href="http://www.erlang.org/doc/man/dialyzer.html">Dialyzer</a></li>
<li><a href="http://www.erlang.org/doc/apps/tools/xref_chapter.html">Xref</a></li>
<li><a href="http://www.erlang.org/doc/apps/common_test/index.html">Common_test</a></li>
</ul>
<p>Most of these test can already be run by customizing the <em>script</em> element in the <em>.travis.yml</em>, but we want to make it as convenient as possible.</p>
<p>Oh, and did you add <a href="http://about.travis-ci.org/">your Erlang project</a> already?</p>
<h2>Questions?</h2>
<p>Questions or need help? Join #travis on freenode or <a href="http://twitter.com/#!/wardbekker">contact me on twitter</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2011/08/19/travis-now-available-in-the-erlang-flavour/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Parallel testing: make your CPU cores sweat</title>
		<link>http://blog.tty.nl/2010/10/20/parallel-testing-make-your-cpu-cores-sweat/</link>
		<comments>http://blog.tty.nl/2010/10/20/parallel-testing-make-your-cpu-cores-sweat/#comments</comments>
		<pubDate>Wed, 20 Oct 2010 06:54:14 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=449</guid>
		<description><![CDATA[All my fellow team-mates have fast workstations: quad core, 8 gigs of memory. Yay! BUT&#8230;..running our full test suite takes about 45 minutes. Boo! It&#8217;s a mix of Cucumber+webrat integration tests and unit tests. If you look at the cpu &#8230; <a href="http://blog.tty.nl/2010/10/20/parallel-testing-make-your-cpu-cores-sweat/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>All my fellow team-mates have fast workstations: quad core, 8 gigs of memory. Yay! BUT&#8230;..running our full test suite takes about 45 minutes. Boo! It&#8217;s a mix of <a href="http://cukes.info/">Cucumber</a>+<a href="http://github.com/brynary/webrat/wiki">webrat</a> integration tests and unit tests. If you look at the cpu activity it doesn&#8217;t even spike a single core during the test. Memory consumption stays practically flat. That&#8217;s an extremely poor use of all that computing power. No wonder, all test are run sequentially. In our multi-core age that&#8217;s soo 90&#8242;s.</p>
<p>The solution is obvious: you need to parallelize the tests. Every integration test needs a dedicated environment to able to get predictable results. For most integration test this means exclusive access to resources like your database (Mysql), memory caching (Memcached) and/or full text search solutions (Sphinx | Solr). You can design your tests to be collision free, but like most multi-threaded programming that uses shared resource it&#8217;s <a href="http://blogs.msdn.com/b/jmstall/archive/2008/01/30/why-threading-is-hard.aspx">quite difficult to get it right</a>. And debugging weird threading issues will make you want to put pencils in your eyes. Trust me on that.</p>
<p>A more efficient way of creating a dedicated environment for every test is the use of <a href="http://en.wikipedia.org/wiki/Virtual_machine">virtual machines</a> (vm). You replicate your integration test environment on a vm. Make several clones and your now have a pool of vm&#8217;s that can run your tests in parallel and guaranteed exclusivity.</p>
<p>The hard part of this solution;</p>
<ul>
<li>Cucumber and the unit test runner need to be modified to run tests distributed.</li>
<li>Non-<a href="http://en.wikipedia.org/wiki/Hypervisor">hypervisor virtualisation systems</a> like <a href="http://www.virtualbox.org/">Virtualbox</a> and <a href="http://www.vmware.com/products/server/">VMWare Server</a> introduce a significant performance overhead. Hypervisor systems require a dedicated box.</li>
<li>Provisioning of virtual machines can be a chore. Solutions like <a href="http://vagrantup.com/">Vagrant</a> can help with that.</li>
</ul>
<p>But it will be worth it. Your CPU cores are worth it.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2010/10/20/parallel-testing-make-your-cpu-cores-sweat/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Timelaps video of the scrumboard during our latest scrum sprint</title>
		<link>http://blog.tty.nl/2010/10/08/timelaps-video-of-the-scrumboard-during-our-latest-scrum-sprint/</link>
		<comments>http://blog.tty.nl/2010/10/08/timelaps-video-of-the-scrumboard-during-our-latest-scrum-sprint/#comments</comments>
		<pubDate>Fri, 08 Oct 2010 08:05:37 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=446</guid>
		<description><![CDATA[We&#8217;ve recorded a timelaps video of the progress on our scrumboard and burdown chart. We&#8217;ve been hacking away for thirteen 9-day sprints with (now) three teams on the San Diego project of VNU Media (link in dutch).]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve recorded a timelaps video of the progress on our scrumboard and burdown chart. We&#8217;ve been hacking away for thirteen 9-day sprints with (now) three teams on the<a href="http://recruitmentmatters.nl/2010/08/09/vnus-san-diego-een-sneak-preview/"> San Diego project of VNU Media</a> (link in dutch).</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/0ZjdrYLKOdM?fs=1&amp;hl=en_US" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/0ZjdrYLKOdM?fs=1&amp;hl=en_US" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2010/10/08/timelaps-video-of-the-scrumboard-during-our-latest-scrum-sprint/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Solr DataImportHandler issue: positive integers indexed for string &#8216;nested&#8217; fields</title>
		<link>http://blog.tty.nl/2010/06/23/solr-dataimporthandler-issue-positive-integers-indexed-for-string-nested-fields/</link>
		<comments>http://blog.tty.nl/2010/06/23/solr-dataimporthandler-issue-positive-integers-indexed-for-string-nested-fields/#comments</comments>
		<pubDate>Wed, 23 Jun 2010 12:43:53 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=392</guid>
		<description><![CDATA[A quick note about a Solr issue that took me some time to solve. If this sounds familiar&#8230;. You are using the DataImportHandler for Solr You have a entity with a field which values come from a related entity. After &#8230; <a href="http://blog.tty.nl/2010/06/23/solr-dataimporthandler-issue-positive-integers-indexed-for-string-nested-fields/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>A quick note about a <a href="http://lucene.apache.org/solr/">Solr</a> issue that took me some time to solve.</p>
<p>If this sounds familiar&#8230;.</p>
<ul>
<li>You are using the <a href="http://wiki.apache.org/solr/DataImportHandler">DataImportHandler for Solr</a></li>
<li>You have a entity with a field which values come from a related entity.</li>
<li>After an import it looks like Solr only indexed even postive integers if you look at the schema browser.</ul>
</ul>
<p>&#8230;.You probably have a &#8216;nested&#8217; field which name is similar to it&#8217;s entity name. See the code below: entity name = regio and field name = regio. Changing field name to something else (regions) solved the issue. When you think about it, it&#8217;s somewhat logical that you don&#8217;t allow field names to have the same name as the entity. An schema exception during indexing would have been nice though.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;dataConfig&gt;<br />
&lt;dataSource type=&quot;JdbcDataSource&quot; driver=&quot;com.mysql.jdbc.Driver&quot; url=&quot;jdbc:mysql://localhost/nvb?zeroDateTimeBehavior=convertToNull&quot; user=&quot;****&quot; password=&quot;****&quot;/&gt;<br />
&nbsp; &nbsp; &lt;document name=&quot;vacatures&quot;&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;entity name=&quot;vacature&quot; query=&quot;select * from vacature&quot;&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;field column=&quot;id&quot; name=&quot;id&quot; /&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;entity name=&quot;regio&quot; query=&quot;SELECT regio from foo where vacature='${vacature.id}&quot;&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;field name=&quot;regio&quot; column=&quot;regio&quot; /&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/entity&gt;</div></div>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2010/06/23/solr-dataimporthandler-issue-positive-integers-indexed-for-string-nested-fields/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zend SOAP Server Webservice quickstart</title>
		<link>http://blog.tty.nl/2010/06/21/zend-soap-server-webservice-quickstart/</link>
		<comments>http://blog.tty.nl/2010/06/21/zend-soap-server-webservice-quickstart/#comments</comments>
		<pubDate>Mon, 21 Jun 2010 18:38:01 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=372</guid>
		<description><![CDATA[Below a quick writeup of my first impression with building an basic Zend Soap Webservice. I invite you to add spelling and grammer corrections in the comments for my education. Starting point My team needs to implement a SOAP service &#8230; <a href="http://blog.tty.nl/2010/06/21/zend-soap-server-webservice-quickstart/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Below a quick writeup of my first impression with building an basic Zend Soap Webservice. I invite you to add spelling and grammer corrections in the comments for my education.</p>
<h2>Starting point</h2>
<ul>
<li>My team needs to implement a <a href="http://en.wikipedia.org/wiki/SOAP">SOAP service</a> for mass posting of vacancies to a job board system.</li>
<li>The SOAP service is based on a <a href="http://www.w3.org/TR/wsdl">WSDL</a> of an existing service. So we’ll use these specifications as a starting point for the proof of concept.</li>
<li>On a site-note: I prefer <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST</a> above SOAP, because of it’s elegant simplicity. But it wouldn’t make a lot of business sense in this case because a lot of, paying, consumers of the new service have working code for the old service. Adapting to a slightly changed SOAP service will be much easier than a switch to a brand new REST API.</li>
</ul>
<h2>Available SOAP Server extensions for PHP</h2>
<p>There are several frameworks / extensions / toolkits for creating a SOAP server for PHP:</p>
<ul>
<li><a title="pear soap" href="http://pear.php.net/package/SOAP">Pear SOAP package</a>. Probably an orphan package as it’s not updated since 2008 and has a beta status. You probably want to look at the alternatives.</li>
<li><a title="nuSoap SOAP toolkit" href="http://sourceforge.net/projects/nusoap/">NuSoap SOAP toolkit</a>.  Started in 2002 and still under active development as the last release was just a few months ago at the time of writing.</li>
<li><a title="PHP 5 SOAP extensions" href="http://www.php.net/manual/en/class.soapserver.php">PHP 5 SOAP extensions</a>. The official SOAP extension for PHP since version 5.</li>
<li><a title="Zend SOAP Server" href="http://framework.zend.com/manual/en/zend.soap.server.html">Zend SOAP Server</a>. Part of the Zend Framework, so probably not very useful if that’s not your current PHP framework.</li>
</ul>
<p>As we use the <a href="http://framework.zend.com/">Zend framework</a> for this project, it was a natural choice to use it’s SOAP server implementation. We might opt for one of the alternatives if we slam into a brick wall later down the line.</p>
<h2>Testing the waters</h2>
<p>The steps I’ve taken to get a basic Zend SOAP Server based on the WSDL up and running</p>
<p><span style="font-size: 13.3333px; "> </span></p>
<ul>
<li>I copied the wdsl to the /public directory of the Zend framework application making it publicly accessible under  http://example.org/jobtool.wsdl</li>
<li>I created a new controller under application/controllers/soapController.php with an public indexAction function. <a href="http://gist.github.com/446932">Example code</a></li>
<li>The new SOAP service is now available under http://example.org/soap</li>
<li>Next step: actually handle SOAP requests. <a href="http://gist.github.com/446938">Example code</a>. Handling of the soap request is as expected: SOAP method arguments are passed as function arguments. Complex types are represented as a <a href="http://stackoverflow.com/questions/931407/what-is-stdclass-in-php">stdClass objects</a>, which basically are associative arrays. Nested complex types are translated to nested stdClass instances. You don&#8217;t get any warnings or exceptions if your argument count is different than specified in the SOAP request. IMHO that&#8217;s undesirable. I rather have big fat ugly exceptions in that case than subtle bugs.  The associative array you return are translated to the complex type as specified in the WSDL and returned to the client.</li>
<li>To test the SOAP service without the need for a full-blown client i’ve used the free <a href="http://sourceforge.net/projects/soapui/">soapUI</a> tool. You point this tool to the WDSL and it automatically creates fake soap request that you can use to test your brand new SOAP services. Make sure you specified the correct urls in the soapAction attributes in the WSDL.</li>
</ul>
<h2>Closing Thoughts</h2>
<p>I hope this post saved you some when time building your first SOAP Webservice using Zend Framework. I don’t know yet from experience if the Zend SOAP Server will handle more advanced scenario’s. Only time will tell. Let me know how it works for you.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2010/06/21/zend-soap-server-webservice-quickstart/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Simple ranked text search for MongoDB</title>
		<link>http://blog.tty.nl/2010/02/08/simple-ranked-text-search-for-mongodb/</link>
		<comments>http://blog.tty.nl/2010/02/08/simple-ranked-text-search-for-mongodb/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 14:46:28 +0000</pubDate>
		<dc:creator>Ward Bekker</dc:creator>
				<category><![CDATA[Open Source Projects]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Software Engineering]]></category>

		<guid isPermaLink="false">http://blog.tty.nl/?p=329</guid>
		<description><![CDATA[In this code snippit you can see how to do a basic ranked text search for MongoDB. The code relies on two simple mapreduce operations. One to create an inverted index from some demo text, and a second one to &#8230; <a href="http://blog.tty.nl/2010/02/08/simple-ranked-text-search-for-mongodb/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>In this <a href="http://gist.github.com/298175">code snippit</a> you can see how to do a basic ranked text search for <a href="http://www.mongodb.org">MongoDB</a>. The code relies on two simple mapreduce operations. One to create an<a href="http://en.wikipedia.org/wiki/Inverted_index"> inverted index</a> from some demo text, and a second one to score the matching documents based on query term hits.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tty.nl/2010/02/08/simple-ranked-text-search-for-mongodb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

