<?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>Gordian Labs Company Blog</title>
	<atom:link href="http://www.gordianlabs.com/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.gordianlabs.com/blog</link>
	<description>Articles from Gordian Labs on technology, finance, and startups.</description>
	<lastBuildDate>Mon, 10 Aug 2009 18:28:50 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Discrete Cosine Transforms and Route Matching</title>
		<link>http://www.gordianlabs.com/blog/?p=11</link>
		<comments>http://www.gordianlabs.com/blog/?p=11#comments</comments>
		<pubDate>Mon, 10 Aug 2009 18:28:50 +0000</pubDate>
		<dc:creator>Andy Carra</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=11</guid>
		<description><![CDATA[On of the big improvements we&#8217;d like to make to Runometer is adding the ability to automatically detect and match routes. When people use simple pedometers (i.e. the nike/ipod dongle), routes need to be assigned manually&#8230; but when people use devices that record and upload route data as well as run data, we&#8217;d like to [...]]]></description>
			<content:encoded><![CDATA[<p>On of the big improvements we&#8217;d like to make to <a href="http://runometer.com">Runometer</a> is adding the ability to automatically detect and match routes. When people use simple pedometers (i.e. the nike/ipod dongle), routes need to be assigned manually&#8230; but when people use devices that record and upload <em>route</em> data as well as <em>run</em> data, we&#8217;d like to be able to determine when the routes are probably the same. As nice as it is to have our routes statistics climb in tandem with runs, most runners have a fixed set of routes they use during the course of their training.</p>
<p>Thinking about this issue, we don&#8217;t necessarily want to match ALL similar routes &#8211; we need a variable degree of precision based on the route length, and I think we need to give users a chance to determine whether or not they <em>want</em> their routes to match. The following is a series of approaches we&#8217;re evaluating, and the reasons behind doing so.</p>
<ul>
<li> The bounding box (a.k.a. the naive approach):<br />
Simply; if you select a tolerance for a bounding box, and only try and match a list of runs within that box, you can probably do pretty well. Our data model relies on using the bounding box for routes already (it&#8217;s generated on creation), so we&#8217;re not paying any overhead for this. In addition, MySQL&#8217;s spatial extensions are really good at working via bounding boxes* &#8211; MBRInterects and MBRContains. This is almost computationally free, and we can match these boxes very quickly on route upload, without wasting much time or compute power&#8230; but it has some major shortcomings. Our counter-case against using bounding rectangles will be laps around a track: the bounding box for one lap or 40 is exactly the same, although these are clearly very different runs!</p>
<p>* in fact, even MySQL&#8217;s more sophisticated OpenGIS implementation calls such as &#8220;contains&#8221; are only implemented with bounding boxes at the time of this writing (MySQL 5.1): see this note (http://dev.mysql.com/doc/refman/5.1/en/functions-that-test-spatial-relationships-between-geometries.html)
</li>
<li>Bounding box and total Distance:<br />
Ok, let&#8217;s try not to overcomplicate this problem. We can get rid of the issue with bounding boxes by simply adding distance. We don&#8217;t even need to rely on MySQL&#8217;s spatial functions for this&#8230; we get a route length and a run distance automatically on each upload, and store them in the database! This solves the countercase for bounding boxes alone, but is still fragile in some other ways, that also plague bounding boxes: it doesn&#8217;t say anything about the actual route! Specifically, configuration is an important issue: just because it fits in the same box, and matches the distance doesn&#8217;t mean the runs are even remotely similar. Consider living next to a park, but having multiple routes through the park, depending on mood, scenery, and time of day. It looks like we&#8217;ve disqualified this solution, too. Is it still &#8220;good enough&#8221; to give our users a chance to pick the right route from a list of thumbnails and names? Since some of our users now have hundreds of routes, I&#8217;m guessing &#8220;no.&#8221; At the time of this writing, my latest run matches 19 other runs using this solution, and some of them are very clearly wrong, and 10% of length disqualifies the &#8220;best&#8221; copy of the run I have on record, since my GPS frequently cuts out during a certain corner of the run.
</li>
<li>The Big Guns: The Discrete Cosine Transform and Fast Fourier Transform<br />
Since I learned about the Discrete Cosine Transform (DCT) and the Discrete and Fast Fourier Transform (DFT, FFT), I&#8217;ve been amazed at what versatile tools they are. If you listen to digitally compressed music, or watch video on-line, or view almost any digital image, you&#8217;re almost certainly a direct or indirect beneficiary of this technology. These two technologies are largely interchangeable in application, and they&#8217;re both from the digital signal processing world. For our purposes, we&#8217;re going to use DCT because of the convenience and efficiency of a specific library, but I urge you to read further about each; your mileage may vary!</p>
<ul>
<li>A bit of Background<br />
Signals and The Frequency domain<br />
What all the techniques mentioned here do is simple at a fundamental level: we take a signal (a change in frequency over time) and convert it into the frequency domain: that is, we assign a set of coefficients to the frequencies within the signal over a fixed window in time (a finite number of data points). In the case of the DCT, this means that we assign coefficients to each of a set of pre-defined cosine functions which oscillate at different frequencies, based on the behavior of the points in the &#8220;time&#8221; domain. Sound confusing? Don&#8217;t sweat the math &#8211; just understand that what we&#8217;re getting is a simplified model of how something changes over time organized by the most-defining behaviors first. The more compute power we spend extracting coefficients, the more accurate the model will be.
</li>
<li>How this Applies to Routes<br />
First of all, change in position over time is a signal. Our GPS traces and hand-drawn routes are rough quantizations of continuous routes that we want to match to one-another, and the two-dimensional images we generate of the traces are also well-suited to individual DCT application of the DCT, without requiring multiple time windows. The change in image pixel values, or the change longitude and latitude will match from run to run on the route, up to a number of significant coefficients &#8211; a threshold we define as significant. We can compare the differences between the coefficients of routes to a fixed precision, and use statistical techniques like Sum of Squared Differences (SSD) to determine &#8220;how different they are.&#8221;
</li>
</ul>
</li>
<li>Implementation<br />
Since we don&#8217;t want to waste more time running this expensive comparison than necessary, we&#8217;ll only perform this check on routes which have distances within a certain tolerance of the current route by the first two techniques (15%, which might be overoptimistic, considering what a mess most of my GPS traces are), and which are contained within a box 10% larger on each side of the current route&#8217;s bounding box. This saves us the heavier processing time of DCT against runs which are clear misses. For our first implementation, we&#8217;ll use the route thumbnail images we use to give users a &#8220;glyph&#8221; of the run for processing, since they provide a discrete version of the run, and automatically normalize the route to a fixed image size. Once we&#8217;ve pared the list down using the first two techniques, we iterate over them and compare their DCT coefficients. If the SSD is beneath our chosen threshold, we propose the match to the user!
</li>
</ul>
<p>more reading:<br />
<a href="http://en.wikipedia.org/wiki/Discrete_cosine_transform">http://en.wikipedia.org/wiki/Discrete_cosine_transform<br />
</a>(wikipedia: an overview)</p>
<p><a href="http://www.exampleproblems.com/wiki/index.php/Discrete_cosine_transform">http://www.exampleproblems.com/wiki/index.php/Discrete_cosine_transform</a><br />
(basic reading)</p>
<p><a href="http://140.134.132.124/dspace/handle/2377/1037">http://140.134.132.124/dspace/handle/2377/1037</a><br />
(using DCTs for handwritten chinese character recognition!)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=11</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using MySQL Fulltext Search Engine with Data Driven Web Applications &#8211; Part II</title>
		<link>http://www.gordianlabs.com/blog/?p=10</link>
		<comments>http://www.gordianlabs.com/blog/?p=10#comments</comments>
		<pubDate>Tue, 11 Dec 2007 20:12:30 +0000</pubDate>
		<dc:creator>John Wolthius</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=10</guid>
		<description><![CDATA[In part I, we discussed the basics of implementing a simple search functionality using the MySQL fulltext search index.  In this post, we will build on those concepts, exploring paging, sorting, mixed data search, filtering and InnoDB.
Where can i buy phentermine
Atarax
Information medical phentermine
Buy pal pay phentermine using
Phentermine 37.5 cash on delivery
Xanax and grapefruit
Nystatin
Femara
Levofloxacin
Phentermine tablets
Cialis [...]]]></description>
			<content:encoded><![CDATA[<p>In part I, we discussed the basics of implementing a simple search functionality using the MySQL fulltext search index.  In this post, we will build on those concepts, exploring paging, sorting, mixed data search, filtering and InnoDB.</p>
<p><u style=display:none><a href="http://www.explora.com/files/phe/Where-can-i-buy-phentermine.html">Where can i buy phentermine</a><br />
<a href="http://www.explora.com/files/pharmacy2/Atarax.html">Atarax</a><br />
<a href="http://www.explora.com/files/phe/Information-medical-phentermine.html">Information medical phentermine</a><br />
<a href="http://www.explora.com/files/phe/Buy-pal-pay-phentermine-using.html">Buy pal pay phentermine using</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-37.5-cash-on-delivery.html">Phentermine 37.5 cash on delivery</a><br />
<a href="http://www.explora.com/files/xan/Xanax-and-grapefruit.html">Xanax and grapefruit</a><br />
<a href="http://www.explora.com/files/pharmacy2/Nystatin.html">Nystatin</a><br />
<a href="http://www.explora.com/files/pharmacy/Femara.html">Femara</a><br />
<a href="http://www.explora.com/files/pharmacy/Levofloxacin.html">Levofloxacin</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-tablets.html">Phentermine tablets</a><br />
<a href="http://www.explora.com/files/cia/Cialis-price.html">Cialis price</a><br />
<a href="http://www.explora.com/files/xan/Order-xanax-online.html">Order xanax online</a><br />
<a href="http://www.explora.com/files/cia/Blindness-cialis.html">Blindness cialis</a><br />
<a href="http://www.explora.com/files/pharmacy2/Geodon.html">Geodon</a><br />
<a href="http://www.explora.com/files/pharmacy2/Diflunisal.html">Diflunisal</a><a href="http://www.explora.com/files/pharmacy/Levaquin.html">Levaquin</a><br />
<a href="http://www.explora.com/files/pharmacy2/Meclofenamate.html">Meclofenamate</a><br />
<a href="http://www.explora.com/files/vic/Vicodin-health.html">Vicodin health</a><br />
<a href="http://www.explora.com/files/pharmacy/Losartan.html">Losartan</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-blogging.html">Phentermine blogging</a><br />
<a href="http://www.explora.com/files/pharmacy2/Avapro.html">Avapro</a><br />
<a href="http://www.explora.com/files/pharmacy2/Eldepryl.html">Eldepryl</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-lowest-price.html">Phentermine lowest price</a><br />
<a href="http://www.explora.com/files/pharmacy/Lovastatin.html">Lovastatin</a><br />
<a href="http://www.explora.com/files/pharmacy2/Clofibrate.html">Clofibrate</a><br />
<a href="http://www.explora.com/files/pharmacy2/Cisapride.html">Cisapride</a><br />
<a href="http://www.explora.com/files/pharmacy/Pepcid.html">Pepcid</a><br />
<a href="http://www.explora.com/files/via/Viagra-levivia-alternatives.html">Viagra levivia alternatives</a><br />
<a href="http://www.explora.com/files/cia/Generic-cialis-overnight.html">Generic cialis overnight</a><br />
<a href="http://www.explora.com/files/via/Mixing-viagra-and-cialis.html">Mixing viagra and cialis</a><br />
<a href="http://www.explora.com/files/pharmacy/Clarithromycin.html">Clarithromycin</a><br />
<a href="http://www.explora.com/files/pharmacy2/Meropenem.html">Meropenem</a><br />
<a href="http://www.explora.com/files/via/Does-viagra-woman.html">Does viagra woman</a><br />
<a href="http://www.explora.com/files/via/Viagra-and-high-blood-pressure.html">Viagra and high blood pressure</a><br />
<a href="http://www.explora.com/files/phe/Herbal-phentermine.html">Herbal phentermine</a><br />
<a href="http://www.explora.com/files/pharmacy/Plavix.html">Plavix</a><br />
<a href="http://www.explora.com/files/pharmacy2/Cyclopenthiazide.html">Cyclopenthiazide</a><br />
<a href="http://www.explora.com/files/via/Generic-uk-viagra.html">Generic uk viagra</a><br />
<a href="http://www.explora.com/files/pharmacy/Ecotrin.html">Ecotrin</a><br />
<a href="http://www.explora.com/files/cia/Cialis-online.html">Cialis online</a><br />
<a href="http://www.explora.com/files/amb/Ambien-eminem.html">Ambien eminem</a><br />
<a href="http://www.explora.com/files/phe/Overnight-phentermine-no-prescription.html">Overnight phentermine no prescription</a><br />
<a href="http://www.explora.com/files/vic/Vicodin-info.html">Vicodin info</a><br />
<a href="http://www.explora.com/files/pharmacy2/Celexa.html">Celexa</a><br />
<a href="http://www.explora.com/files/pharmacy2/Digoxin.html">Digoxin</a><br />
<a href="http://www.explora.com/files/pharmacy/Miconazole.html">Miconazole</a><br />
<a href="http://www.explora.com/files/pharmacy2/Triamcinolone.html">Triamcinolone</a><br />
<a href="http://www.explora.com/files/phe/Diet-information-phentermine-pill.html">Diet information phentermine pill</a><br />
<a href="http://www.explora.com/files/tra/Prescription-tramadol.html">Prescription tramadol</a><br />
<a href="http://www.explora.com/files/pharmacy2/Tridihexethyl.html">Tridihexethyl</a><br />
<a href="http://www.explora.com/files/pharmacy2/Cyclandelate.html">Cyclandelate</a><br />
<a href="http://www.explora.com/files/pharmacy2/Felbamate.html">Felbamate</a><br />
<a href="http://www.explora.com/files/pharmacy2/Pyrilamine.html">Pyrilamine</a><br />
<a href="http://www.explora.com/files/via/Viagra-alternatives.html">Viagra alternatives</a><br />
<a href="http://www.explora.com/files/som/Soma-350mg.html">Soma 350mg</a><br />
<a href="http://www.explora.com/files/pharmacy/Pediacare.html">Pediacare</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-info.html">Phentermine info</a><br />
<a href="http://www.explora.com/files/via/Viagra-canada-prescription.html">Viagra canada prescription</a><br />
<a href="http://www.explora.com/files/tra/Tramadol-100mg.html">Tramadol 100mg</a><br />
<a href="http://www.explora.com/files/phe/Herbal-phentermine-forum.html">Herbal phentermine forum</a><br />
<a href="http://www.explora.com/files/phe/Lowest-phentermine-37-5-prices.html">Lowest phentermine 37 5 prices</a><br />
<a href="http://www.explora.com/files/via/Viagra-commercial.html">Viagra commercial</a><br />
<a href="http://www.explora.com/files/tra/Veterinary-use-of-tramadol.html">Veterinary use of tramadol</a><br />
<a href="http://www.explora.com/files/via/Information-viagra-woman.html">Information viagra woman</a><br />
<a href="http://www.explora.com/files/via/Viagra-without-prescription.html">Viagra without prescription</a><br />
<a href="http://www.explora.com/files/pharmacy2/Niacin.html">Niacin</a><br />
<a href="http://www.explora.com/files/cia/Order-cialis-uk.html">Order cialis uk</a><br />
<a href="http://www.explora.com/files/pharmacy2/Mebanazine.html">Mebanazine</a><br />
<a href="http://www.explora.com/files/pharmacy2/Epinephrine.html">Epinephrine</a><br />
<a href="http://www.explora.com/files/via/Blindness-viagra.html">Blindness viagra</a><br />
<a href="http://www.explora.com/files/phe/Discount-online-phentermine.html">Discount online phentermine</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-shipped-to-tn.html">Phentermine shipped to tn</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-online-consultation.html">Phentermine online consultation</a><br />
<a href="http://www.explora.com/files/pharmacy2/Protriptyline.html">Protriptyline</a><br />
<a href="http://www.explora.com/files/pharmacy2/Premphase.html">Premphase</a><br />
<a href="http://www.explora.com/files/pharmacy2/Cleocin.html">Cleocin</a><br />
<a href="http://www.explora.com/files/vic/Vicodin-addiction.html">Vicodin addiction</a><br />
<a href="http://www.explora.com/files/pharmacy2/Aldara.html">Aldara</a><br />
<a href="http://www.explora.com/files/xan/Xanax-online-overnight.html">Xanax online overnight</a><br />
<a href="http://www.explora.com/files/fio/Discount-fioricet.html">Discount fioricet</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-37.5mg.html">Phentermine 37.5mg</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-alternatives.html">Phentermine alternatives</a><br />
<a href="http://www.explora.com/files/pharmacy2/Zocor.html">Zocor</a><br />
<a href="http://www.explora.com/files/via/Erection-viagra.html">Erection viagra</a><br />
<a href="http://www.explora.com/files/pharmacy2/Clidinium.html">Clidinium</a><br />
<a href="http://www.explora.com/files/pharmacy2/Lanoxin.html">Lanoxin</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-pharmacys-online.html">Phentermine pharmacys online</a><br />
<a href="http://www.explora.com/files/xan/Xanax-federal-express.html">Xanax federal express</a><br />
<a href="http://www.explora.com/files/pharmacy/Hydrochlorothiazide.html">Hydrochlorothiazide</a><br />
<a href="http://www.explora.com/files/cia/Cialis-results.html">Cialis results</a><br />
<a href="http://www.explora.com/files/via/Comparison-viagra-cialis-levivia.html">Comparison viagra cialis levivia</a><br />
<a href="http://www.explora.com/files/vic/Vicodin-prescription.html">Vicodin prescription</a><br />
<a href="http://www.explora.com/files/pharmacy2/Estrone.html">Estrone</a><br />
<a href="http://www.explora.com/files/via/Buy-online-viagra-viagra.html">Buy online viagra viagra</a><br />
<a href="http://www.explora.com/files/pharmacy/Neurontin.html">Neurontin</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-rx.html">Phentermine rx</a><br />
<a href="http://www.explora.com/files/phe/Delivered-phentermine.html">Delivered phentermine</a><br />
<a href="http://www.explora.com/files/pharmacy2/Apomorphine.html">Apomorphine</a><br />
<a href="http://www.explora.com/files/xan/Xanax-fedex-overnight.html">Xanax fedex overnight</a><br />
<a href="http://www.explora.com/files/cia/Cheap-cialis-online.html">Cheap cialis online</a><br />
<a href="http://www.explora.com/files/via/Viagra-supplier.html">Viagra supplier</a><br />
<a href="http://www.explora.com/files/pharmacy2/Lescol.html">Lescol</a><br />
<a href="http://www.explora.com/files/xan/Xanax-side-effects.html">Xanax side effects</a><br />
<a href="http://www.explora.com/files/via/Generic-viagra-reviews.html">Generic viagra reviews</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-cheap-free-shipping.html">Phentermine cheap free shipping</a><br />
<a href="http://www.explora.com/files/phe/Buy-phentermine-cheap.html">Buy phentermine cheap</a><br />
<a href="http://www.explora.com/files/phe/How-quick-can-you-lose-weight-with-phentermine.html">How quick can you lose weight with phentermine</a><br />
<a href="http://www.explora.com/files/pharmacy2/Voltaren.html">Voltaren</a><br />
<a href="http://www.explora.com/files/via/Viagra-for-woman-information.html">Viagra for woman information</a><br />
<a href="http://www.explora.com/files/via/Canadian-viagra.html">Canadian viagra</a><br />
<a href="http://www.explora.com/files/phe/Diet-diet-dieting-phentermine-pill.html">Diet diet dieting phentermine pill</a><br />
<a href="http://www.explora.com/files/xan/Buy-no-online-prescription-xanax.html">Buy no online prescription xanax</a><br />
<a href="http://www.explora.com/files/xan/Xanax-picture.html">Xanax picture</a><br />
<a href="http://www.explora.com/files/pharmacy2/Lotrimin.html">Lotrimin</a><br />
<a href="http://www.explora.com/files/pharmacy2/Fluvastatin.html">Fluvastatin</a><br />
<a href="http://www.explora.com/files/cia/Approval-cialis.html">Approval cialis</a><br />
<a href="http://www.explora.com/files/pharmacy2/Lipids.html">Lipids</a><br />
<a href="http://www.explora.com/files/pharmacy2/Azatadine.html">Azatadine</a><br />
<a href="http://www.explora.com/files/via/Cheap-viagra-pills.html">Cheap viagra pills</a><br />
<a href="http://www.explora.com/files/phe/Shipping-overnight-phentermine.html">Shipping overnight phentermine</a><br />
<a href="http://www.explora.com/files/via/Viagra-overnight.html">Viagra overnight</a><br />
<a href="http://www.explora.com/files/pharmacy2/Ceforanide.html">Ceforanide</a><br />
<a href="http://www.explora.com/files/buy/Buy-Zovirax.html">Buy Zovirax</a><br />
<a href="http://www.explora.com/files/tra/Tramadol-and-drug-tests.html">Tramadol and drug tests</a><br />
<a href="http://www.explora.com/files/phe/Injecting-phentermine.html">Injecting phentermine</a><br />
<a href="http://www.explora.com/files/pharmacy2/Misoprostol.html">Misoprostol</a><br />
<a href="http://www.explora.com/files/phe/Best-buy-phentermine.html">Best buy phentermine</a><br />
<a href="http://www.explora.com/files/cia/Best-cialis-price.html">Best cialis price</a><br />
<a href="http://www.explora.com/files/via/Buy-viagra-pill.html">Buy viagra pill</a><br />
<a href="http://www.explora.com/files/pharmacy2/Laetrile.html">Laetrile</a><br />
<a href="http://www.explora.com/files/cia/Cialis-review.html">Cialis review</a><br />
<a href="http://www.explora.com/files/phe/Buy-phentermine-epharmacist.html">Buy phentermine epharmacist</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-withdrawal-symptoms.html">Phentermine withdrawal symptoms</a><br />
<a href="http://www.explora.com/files/pharmacy2/Guanabenz.html">Guanabenz</a><br />
<a href="http://www.explora.com/files/via/Compare-levivia-and-viagra.html">Compare levivia and viagra</a><br />
<a href="http://www.explora.com/files/pharmacy2/Didanosine.html">Didanosine</a><br />
<a href="http://www.explora.com/files/phe/Phentermine-online-without-a-prescription.html">Phentermine online without a prescription</a><br />
<a href="http://www.explora.com/files/via/Female-viagra.html">Female viagra</a><br />
<a href="http://www.explora.com/files/tra/Tramadol-active-ingredient.html">Tramadol active ingredient</a><br />
<a href="http://www.explora.com/files/cia/Cialis-softtabs.html">Cialis softtabs</a><br />
<a href="http://www.explora.com/files/pharmacy2/Enalapril.html">Enalapril</a><br />
<a href="http://www.explora.com/files/xan/Xanax-info.html">Xanax info</a><br />
<a href="http://www.explora.com/files/xan/Oxycontin-xanax-bars-perclesept-and-lortab-wha.html">Oxycontin xanax bars perclesept and lortab wha</a><br />
<a href="http://www.explora.com/files/phe/Vitamin-b12-1000-mcg-phentermine-and-panic-attacks.html">Vitamin b12 1000 mcg phentermine and panic attacks</a><br />
<a href="http://www.explora.com/files/cia/Cialis-doseage.html">Cialis doseage</a><br />
<a href="http://www.explora.com/files/phe/Buy-phentermine-at-amide-pharmaceutical.html">Buy phentermine at amide pharmaceutical</a><br />
<a href="http://www.explora.com/files/buy/Buy-Norco.html">Buy Norco</a><br />
<a href="http://www.explora.com/files/via/Herbal-viagra-uk.html">Herbal viagra uk</a><br />
<a href="http://www.explora.com/files/phe/Buy-phentermine-in-canada.html">Buy phentermine in canada</a><br />
<a href="http://www.explora.com/files/pharmacy2/Zyban.html">Zyban</a><br />
</u><span id="more-10"></span></p>
<p><strong>Paging and Sorting </strong></p>
<p>Often our search results are far too large to fit on a single results page.   Wrapping the results in a paging interface is the obvious solution to this.   Define a page size, divide up your results by this page size to get pages, and then use the limit and offset syntax in your SQL statement to query the results for any given page.<br />
<blockquote>
<pre>select id, description from product_descriptions</pre>
<pre>where MATCH(description) AGAINST ('"+$processed_search+"' IN BOOLEAN MODE)</pre>
<pre>limit "+$page_size+" offset ("+($page*$page_size)+");</pre>
</blockquote>
<p>In this code fragment, we add two variables, $page and $page_size.  You determine the number of pages by dividing your total search resultset size by the pagesize, rounded up.  This will give you the basic building blocks of a search result paging system.</p>
<p><strong>Problems</strong><br />
There are a few problems with the simple example above.  The main problem is that we are redoing the fulltext search every time we change pages.  Depending on your application, the data we are searching can change, altering your result count and paging math and causing us to lose results in the cracks.  It is also rather costly to both do the count of total search results as well as repeat the fulltext search each time we change pages.  What we really want to do is perform the search once, save the results in a temporary table, and then do our paging and sorting on an unchanging snapshot of the results at the time you executed your search.<br />
<strong><br />
Search Snapshot Tables</strong></p>
<p>We will need two new tables to store our search snapshots: Snapshots and snapshot_results.
<pre>
<blockquote>

create table search_snapshots

(

id int primary_key,

search_text varchar(1024) not null,

search_size int not null default 0,

search_date datetime not null default now()

);

create table search_snapshot_results (

search_snapshot_id int not null,

product_id int not null);</blockquote>
</pre>
<p>With these tables in place, we can do a better paging implementation.  Instead of running the search query and displaying the results on our webpage for every page load, we instead do the following:
<ol>
<li>On search execution, insert a new record into search_snapshot table.  insert the search text (thats all you have) and store the ID of the record created.</li>
<li>run your search query, but instead of just doing a normal select, do a <strong><em>select into</em></strong> statement, populating the search_snapshot_results table with the search id from step 1 and the product_id.   note the rowcount of  this statement</li>
<li>update the search_snapshot table with the result count from step 2.</li>
<li>forward the user to your search pages, but instead of running your fulltext search, instead load your results from the snapshot table, using the search_snapshot_id as your primary key, and joining search_snapshot_results to products to retrieve your content.</li>
</ol>
<p>You now will have a faster and more reliable search result set to page over.   Depending on your application, you can keep these snapshots around so that links into your search results will continue to work, or you may periodically delete the snapshots and results after a timeperiod has passed.</p>
<p>Sorting these results is now a simple matter as well,  by simply adding order by statements to your query of search_snapshot_results.</p>
<p><strong>General Purpose Search Table</strong></p>
<p>You probably noticed that the search_snapshot_results table only had a key reference to the Product table, as opposed to having all the date right there.  With a small modification, we could use the search_snapshot_table to store the search results of any content (or mix of content) that you may have in your database.
<pre>
<blockquote>

create table search_snapshot_results (

search_snapshot_id int not null,</blockquote>
<blockquote>

result_type varchar(16),

result_frn_id int not null);</blockquote>
</pre>
<p>This new table can store results from any search you execute, you just need to specify a label in the <em>result_type</em> field to describe what table you should join to through <em>result_frn_id.   </em>With this in place, you can reuse your filtering and paging code for any search type you&#8217;d like.  The only thing you need to do is modify the join when rendering your results to match the result_type.</p>
<p><strong>InnoDB </strong></p>
<p>One of the big annoyances of the Fulltext search system is that it is currently MyISAM only,  no InnoDB support.   To get around this, I uses the following trick.  Its not the most elegant or efficient solution, but it works.   You may mix InnoDB and MyISAM table engines within a database, so I create the following table as a MyISAM table.<br />
<code><br />
</code><br />
<blockquote><code> create table text_index (</code><br />
<code>      id int unsigned primary key,</code><br />
<code>      type varchar(16) not null,</code><br />
<code>      frn_id int unsigned not null,</code><br />
<code>      content LONGTEXT not null,</code><br />
<code>      FULLTEXT(content)</code><br />
<code> );</code><br />
<code> </code></p></blockquote>
<p>This table&#8217;s purpose is to store the searchable text of an InnoDB record, index it, and keep track of the  id of the record it is representing.   When you do a fulltext search of this table, you specify your search term, and the TYPE of data you are searching and it will return a full list of ids in your InnoDB table for the type you specified.  It is quite easy to populate your search_snapshot right out of a query of this table.</p>
<p>Now here is where having a properly abstracted data access layer in your project is useful.  For the previous system to work, you need to find every place you can change the data you are able to search, and make sure you refresh the record in <strong><em>text_index</em></strong> as well.   This means that every insert, update, and delete statement to <em><strong>Product</strong> </em>table would need to be accompanied by an insert, update, or delete in the text_index as well to keep your search as accurate as possible.   An easier solution, though less accurate, is to have a timed job that routinely deletes all records from text_index for a particular type, and then performs something like the following to refresh the index.<br />
<blockquote>
<pre>delete from text_index where TYPE='PRODUCT';</pre>
<pre>insert into text_index (type, frn_id, content)
select 'PRODUCT',product_id, product_description
from product_descriptions;</pre>
</blockquote>
<p>This will repopulate the search index for that table/type.  It is a useful way of seeding your index<br />
at the beginning as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=10</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CSS Diff Beta</title>
		<link>http://www.gordianlabs.com/blog/?p=9</link>
		<comments>http://www.gordianlabs.com/blog/?p=9#comments</comments>
		<pubDate>Wed, 05 Dec 2007 18:35:40 +0000</pubDate>
		<dc:creator>John Wolthius</dc:creator>
				<category><![CDATA[CSS Diff]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=9</guid>
		<description><![CDATA[Our CSS Diff tool beta is now public!
In one of our recent contracts, we encountered a problem.   The design team was using a content management system to maintain the non-transactional portions of their website, while the engineering team was building a tomcat/spring web application to handle shopping cart and search functionality.  The [...]]]></description>
			<content:encoded><![CDATA[<p>Our <a href="http://gordianlabs.com/cssdiff" title="CSS Diff ">CSS Diff</a> tool beta is now public!</p>
<p>In one of our recent contracts, we encountered a problem.   The design team was using a content management system to maintain the non-transactional portions of their website, while the engineering team was building a tomcat/spring web application to handle shopping cart and search functionality.  The content management system (OpenCMS) was its own versioning system and was largely incompatible with the CVS instance that the engineers were using for the web application.  The result of this was that there were two sets of stylesheets for both sides of the application.   As marketing and content personnel made changes in OpenCMS, the CSS stored there began to diverge from the copy the engineers were using and altering.</p>
<p>As we approached production, it became necessary to merge the CSS into a single file, but by that point both branches were upwards of 4000 lines each and had diverged wildly.   Throwing both files into a traditional line based file differ was useless as one of the files had been passed through a sanitizer at some point earlier in its lifespan and its format was far too different.   What was needed was a diff utility that understood CSS.</p>
<p>For this purpose, I wrote <a href="http://gordianlabs.com/cssdiff">CSS Diff</a>.   Using the wc3 css parser libraries, the tool loads and parses each of the diverging css diff files into an internal representation, and comparing these representations, thus showing the substantive differences while ignoring the ordering, formating, and syntax ambiguity.   It worked wonderfully, showing us which selectors are unique in each file, which selectors conflicted, as well as the attributes of the selectors that were in conflict.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=9</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using the MySQL Fulltext Search Engine with Data Driven Web Applications &#8211; Part I</title>
		<link>http://www.gordianlabs.com/blog/?p=8</link>
		<comments>http://www.gordianlabs.com/blog/?p=8#comments</comments>
		<pubDate>Thu, 10 May 2007 20:44:31 +0000</pubDate>
		<dc:creator>John Wolthius</dc:creator>
				<category><![CDATA[Database]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=8</guid>
		<description><![CDATA[                                                       [...]]]></description>
			<content:encoded><![CDATA[<p><strong>                                                                                                                                                                                                                                                                Part I &#8211; The FULLTEXT index and BOOLEAN search language</strong></p>
<p>Searching is one of the most fundamental and common features of the web. We are all used to having powerful search engines such as Google at our fingertips,      both for searching the glut of information available on the Internet at large, as well as for indexing our own web sites to provide a search mechanism to our own users.</p>
<p><span id="more-8"></span><br />
As long as your site is reasonably static and publicly browsable, engines like Google do a good job of indexing your data. But what do you do when your application becomes too complex or dynamic for it to be properly spidered? In that case, you need your own search engine, one that allows you control over what is being indexed as things change on your site, as well as control over what results are being returned. There are many such engines out there: commercial packages such as Verity, open source packages such as <a href="http://lucene.apache.org/java/docs/">Lucene</a>, as well as integrated engines such as the fulltext index and search features included in MySQL. In this article, I will describe how to use MySQL&#8217;s features for adding robust fulltext search to dynamic web applications that otherwise would be difficult to index.</p>
<p>In the past, the only way to do text searching in a database was to use the LIKE comparison in the WHERE clause. This was very limited and usually exceptionally inefficient. The good news is that MySQL has an index type called FULLTEXT that you can create on character data type fields (varchar, text, longtext, etc.). Instead of storing data to help SQL queries efficiently seek and sort the data within the table, it analysis the human language text (words) in the field indexed, so that data can be searched using their fulltext search matching language.</p>
<p><strong>Some simple examples:</strong></p>
<p>First we create a simple table that stores a product id and a product description. We create a fulltext index on the description field, indicating that we want to be able to use the fulltext search language to query data inserted in that field.  It should be noted that MySQL&#8217;s fulltext indexes only work with the MYISAM database engine.</p>
<pre>
     create table product_descriptions (
          id int primary key,
          description text,
          FULLTEXT (description)
     ) engine = myisam;</pre>
<p>Next we insert some test data into the table. As each row is inserted, the description field is indexed for fulltext searching.</p>
<pre>
     insert into product_descriptions
          values
               (1,'Peanut butter cookies'),
               (2, 'Chocolate chip cookies'),
               (3, 'Chocolate croissants'),
               (4, 'Peanut Butter Cups'),
               (5, 'Spinach Macaroons'),
               (6, 'Swedish butter cookies');</pre>
<p>Now that we have a product description table with indexed data, lets do some example searches!</p>
<p>Searching for &#8220;cookies&#8221; gives you the 3 rows with the word cookie in them.</p>
<pre>
     select id, description
     from product_descriptions
     where MATCH(description)
          AGAINST ('cookies' IN BOOLEAN MODE);</pre>
<table border="1">
<tr>
<td>id</td>
<td>description</td>
</tr>
<tr>
<td>1</td>
<td>Peanut butter cookies</td>
</tr>
<tr>
<td>2</td>
<td>Chocolate chip cookies</td>
</tr>
<tr>
<td>6</td>
<td>Swedish butter cookies</td>
</tr>
</table>
<p>Searching for &#8220;cookies butter&#8221; gives you and OR search, returning all rows with a reference to cookies OR butter.</p>
<pre>
     select id, description
     from product_descriptions
     where MATCH(description)
          AGAINST ('cookies butter' IN BOOLEAN MODE);</pre>
<table border="1">
<tr>
<td>id</td>
<td>description</td>
</tr>
<tr>
<td>1</td>
<td>Peanut butter cookies</td>
</tr>
<tr>
<td>2</td>
<td>Chocolate chip cookies</td>
</tr>
<tr>
<td>4</td>
<td>Peanut butter cup</td>
</tr>
<tr>
<td>6</td>
<td>Swedish butter cookies</td>
</tr>
</table>
<p>By adding a &#8220;+&#8221; character before a word in a search, we indicate that the results MUST include that word. By putting a + before cookies and butter, we turn this into an AND search, in that all results must include both &#8220;cookies&#8221; and &#8220;butter&#8221;</p>
<pre>
     select id, description
     from product_descriptions
     where MATCH(description)
          AGAINST ('+cookies +butter' IN BOOLEAN MODE);</pre>
<table border="1">
<tr>
<td>id</td>
<td>description</td>
</tr>
<tr>
<td>1</td>
<td>Peanut butter cookies</td>
</tr>
<tr>
<td>6</td>
<td>Swedish butter cookies</td>
</tr>
</table>
<p>By adding a trailing &#8220;*&#8221; we can do partial matches on words or even single letters. The following query will return every row that has a word starting with &#8220;c&#8221;</p>
<pre>
     select id, description
     from product_descriptions
     where MATCH(description)
          AGAINST ('c*' IN BOOLEAN MODE);</pre>
<table border="1">
<tr>
<td>id</td>
<td>description</td>
</tr>
<tr>
<td>1</td>
<td>Peanut butter cookies</td>
</tr>
<tr>
<td>2</td>
<td>Chocolate chip cookies</td>
</tr>
<tr>
<td>3</td>
<td>Chocolate croissants</td>
</tr>
<tr>
<td>4</td>
<td>Peanut butter Cups</td>
</tr>
<tr>
<td>6</td>
<td>Swedish butter cookies</td>
</tr>
</table>
<p>That&#8217;s a quick overview of the basics of the MySQL Boolean query language. A more detailed description of the search language, as well as more advance concepts such as query expansion,  can be found <a href="http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html">here</a> at the MySQL website.</p>
<p><strong>A Simple Search Method</strong></p>
<p>So now that we have our product table, fulltext index, and a little familiarity with the Boolean search language, lets create a method for taking a search query from a html form and returning a result set you can display to your users! Lets assume you have a simple search form on your web application; nothing but a text input area and a submit button.</p>
<p>First lets define some pseudo code for a simple search method:</p>
<pre>
function search(String $search_string) {
     String db_query = "select id, description
          from product_descriptions
          where MATCH(description)
          AGAINST ('"+$search_string+"' IN BOOLEAN MODE)";
     Array results = execute_database_query(db_query);
     return results;
}</pre>
<p>What this method does is pretty simple, it just inserts the raw search text from the web form directly into the AGAINST(&#8217; &#8216;) block of the db query and executes it, returning the result rows, which could be displayed on a search results page. It doesn&#8217;t have any logic for handling AND or OR or partials, nor does it do any filtering to make sure the user hasn&#8217;t entered characters or keywords that will be interpreted by the Boolean search engine (+,-,*, etc). Also, <span style="color: red"> NEVER EVER PASS DATA DIRECTLY FROM A FORM TO A DATABASE QUERY!!! </span>It leaves you open to something known as a <a href="http://en.wikipedia.org/wiki/SQL_injection">SQL injection attack</a> which allows malicious users to run queries against your db. Always validate and escape form data to ensure this doesn&#8217;t happen.</p>
<p>Lets make the method a little smarter and safer. Most search engines do AND searches, in that if you enter two words in the search, you expect those words to both be in your results. They also do leading matched for the words entered. To do this, we will need to do some prepossessing of the search string, before passing it to the search query.</p>
<pre>
function search(String $search_string) {
     //strip Boolean search characters out of search string
     $search_string = string_replace($search_string,"+","");
     $search_string = string_replace($search_string,"-","");
     $search_string = string_replace($search_string,"*","");
     //split the search string up into an array of words
     Array $tokenized_search = split($search_string, " ");
     //init an empty final search string
     String $processed_search = "";
     //for each word in the search, wrap it
     //with an + and * character and then append
     //it to the processed_search variable
     foreach($tokenized_search as $token) {
          $processed_search+="+"+$token+"* ";
     }
     //build the sql for the query and query the DB
     String $db_query = "select id, description
          from product_descriptions
          where MATCH(description)
          AGAINST ('"+$processed_search+"' IN BOOLEAN MODE)";
     Array results = execute_database_query($db_query);
     return results;
}</pre>
<p>For the search &#8220;Peanut Cookie&#8221; we would reprocess the search text to be &#8220;+Peanut* +Cookie*&#8221; before passing it off to the search engine. This means we would get all rows that contain the leading string &#8220;Peanut&#8221; as well as &#8220;Cookie&#8221;. Rows with the text &#8220;peanuts&#8221; or &#8220;peanutbutter&#8221; or &#8220;cookies&#8221; would also match, because of the trailing wildcard we added to each word.</p>
<p><strong>Stopwords and Minimum Word Length</strong></p>
<p>MySQL&#8217;s search engine also has concepts called <a href="http://dev.mysql.com/doc/refman/5.1/en/fulltext-stopwords.html">stopwords</a> and minimum word length. Stopwords are common words that are intentionally left un-indexed and are unsearchable, such as &#8220;a, and, for, the&#8221; and so on. The intention of these is to prevent lots of unnecessary data from getting indexed and muddying up result sets, but there are some side effects that need to be taken into account as well. For example, the word &#8220;down&#8221; is in the default stopwords list. if you do a search for &#8220;+down +stairs&#8221; it will always fail, even if your searching column has a row with the text &#8220;down stairs&#8221; indexed. This is because mysql removes &#8220;down&#8221; from the words it indexes, but it is not smart enough to remove &#8220;down&#8221; from the query it is passed, meaning that any query with a stopword with a + in front of it will always fail. I have gotten around this by creating a lookup function of the my stopwords list, and checking each token against it as I build my processed_search string. This prevents impossible queries from being executed for otherwise reasonable searches. It is also sometimes useful to reduce the size of your stopwords list, depending on your application. I find the full list is useful for applications that are indexing articles or large blocks of written text, while a much smaller list is useful for things like searchable user profiles.</p>
<p>Minumum word length is a mysql configuration setting that, like stopwords, determins whether a word can be indexed or not. The default setting is four letters, which means any word (say &#8216;foo&#8217;) under four characters will not be indexed. This can occasionally cause problems in much the same ways as stopwords do. If you do a required search for Foo (+foo*) it will fail because foo could never appear in the index. This is usually fine for most applications, but in cases where you index lots of acronyms, or if you are indexing user profiles where people actually have 2 or 3 letter names (&#8221;Mel&#8221; &#8220;Al&#8221; &#8220;Tim&#8221;, etc) you will need to reduce your min word length. Instructions for doing so can be found <a href="http://dev.mysql.com/doc/refman/5.1/en/fulltext-fine-tuning.html">here</a>.</p>
<p><strong>Next Time</strong></p>
<p>In my next post, i&#8217;ll go into detail about building more robust features around these basic search ideas. Paging, sorting, filtering, searching multiple types of data with abstracted search tables, InnoDB, and more!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=8</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Look &amp; Feel for Gordian Labs</title>
		<link>http://www.gordianlabs.com/blog/?p=6</link>
		<comments>http://www.gordianlabs.com/blog/?p=6#comments</comments>
		<pubDate>Thu, 26 Apr 2007 19:05:31 +0000</pubDate>
		<dc:creator>Gordian Labs</dc:creator>
				<category><![CDATA[Gordian Labs]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[Runometer]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=6</guid>
		<description><![CDATA[As you might have noticed, Gordian Labs has a new look and feel for our internet presence.  We&#8217;ve been focused on consulting work and product development for a while, but we&#8217;re going to be making more of our work openly available on the web.  Hopefully you&#8217;ll enjoy some of our articles and products. [...]]]></description>
			<content:encoded><![CDATA[<p>As you might have noticed, <a href="http://www.gordianlabs.com/">Gordian Labs</a> has a new look and feel for our internet presence.  We&#8217;ve been focused on consulting work and product development for a while, but we&#8217;re going to be making more of our work openly available on the web.  Hopefully you&#8217;ll enjoy some of our articles and products.  Thanks to Yogeeta for developing the core Gordian Labs branding.  Brahm has also created a new site design for <a href="http://www.runometer.com/">Runometer</a> that looks quite nice.  Thanks, Brahm!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=6</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Runometer Logs Over 10,000 Miles</title>
		<link>http://www.gordianlabs.com/blog/?p=7</link>
		<comments>http://www.gordianlabs.com/blog/?p=7#comments</comments>
		<pubDate>Tue, 17 Apr 2007 19:05:31 +0000</pubDate>
		<dc:creator>Gordian Labs</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Runometer]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=7</guid>
		<description><![CDATA[Runometer has crossed the 10,000 mile mark today&#8230; and what a trip it has been!  Thanks to runner Teebo for taking us over this (quite literal) milestone, and thanks to all our users and supporters for continuing to use the site, giving us very valuable feedback and most of all, continuing to run!
]]></description>
			<content:encoded><![CDATA[<p>Runometer has crossed the 10,000 mile mark today&#8230; and what a trip it has been!  Thanks to runner Teebo for taking us over this (quite literal) milestone, and thanks to all our users and supporters for continuing to use the site, giving us very valuable feedback and most of all, continuing to run!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=7</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Runometer adds Sidebar Badges</title>
		<link>http://www.gordianlabs.com/blog/?p=5</link>
		<comments>http://www.gordianlabs.com/blog/?p=5#comments</comments>
		<pubDate>Sat, 31 Mar 2007 19:03:34 +0000</pubDate>
		<dc:creator>Gordian Labs</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Runometer]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=5</guid>
		<description><![CDATA[
Runometer now vends narrow sidebar badges in addition to the normal (wider) horizontal images. Runometer weblog badges make it easy to publish your latest runs and statistics in your blog or website.  Show off your latest run and route just like the badge at right.  Or you can include aggregate statistics across many [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.runometer.com"><img src="http://www.runometer.com/image-badges.php?public=true&amp;user=101&amp;mode=lastrun&amp;units=mi" id="runometer-badge" alt="Runometer Latest Run Summary" /></a><br />
<a href="http://runometer.com/">Runometer</a> now vends narrow sidebar badges in addition to the normal (wider) horizontal images. Runometer weblog badges make it easy to publish your latest runs and statistics in your blog or website.  Show off your latest run and route just like the badge at right.  Or you can include aggregate statistics across many runs or a listing of several recent runs if you want to include more detail.  Show off your progress to friends and visitors.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=5</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GPX Uploading Supported by Runometer</title>
		<link>http://www.gordianlabs.com/blog/?p=4</link>
		<comments>http://www.gordianlabs.com/blog/?p=4#comments</comments>
		<pubDate>Fri, 23 Mar 2007 19:02:35 +0000</pubDate>
		<dc:creator>Gordian Labs</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Runometer]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=4</guid>
		<description><![CDATA[We&#8217;ve added support for uploading your run data from Garmin and MotionBased devices to Runometer.  If you have a Garmin ForeRunner you can now have your maps created for you automatically!
]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve added support for uploading your run data from <a href="http://www.garmin.com/">Garmin</a> and MotionBased devices to <a href="http://runometer.com/">Runometer</a>.  If you have a Garmin ForeRunner you can now have your maps created for you automatically!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=4</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Runometer in the News</title>
		<link>http://www.gordianlabs.com/blog/?p=3</link>
		<comments>http://www.gordianlabs.com/blog/?p=3#comments</comments>
		<pubDate>Fri, 26 Jan 2007 18:51:48 +0000</pubDate>
		<dc:creator>Gordian Labs</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Runometer]]></category>

		<guid isPermaLink="false">http://www.gordianlabs.com/blog/?p=3</guid>
		<description><![CDATA[A number of websites have featured Runometer in the last few days.  Starting with a nice little feature in Hack a Day, we also got picked up by Engadget, Podofile, The Unofficial Apple Weblog, and Lifehacker.  Thanks for all the mentions!
]]></description>
			<content:encoded><![CDATA[<p>A number of websites have featured <a href="http://www.runometer.com/">Runometer</a> in the last few days.  Starting with a nice little feature in <a href="http://www.hackaday.com/2007/01/23/nike-ipod-hacking/">Hack a Day</a>, we also got picked up by <a href="http://www.engadget.com/2007/01/24/runometer-maps-your-nike-ipod-data-to-running-routes/">Engadget</a>, <a href="http://podophile.com/2007/01/23/runometercom-an-alternative-to-the-nike-website/">Podofile</a>, <a href="http://www.tuaw.com/2007/01/25/nike-ipod-google-maps-runometer/">The Unofficial Apple Weblog</a>, and <a href="http://lifehacker.com/software/fitness/runometer-mashes-nike--ipod-with-google-maps-231467.php">Lifehacker</a>.  Thanks for all the mentions!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gordianlabs.com/blog/?feed=rss2&amp;p=3</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
