Word segmentation library in Golang

Standard

I’ve been into Golang lately, and today I’m glad to announce my second open source project in Golang, following the feature flags API. My second package is all about word segmentation.

What is the word segmentation problem?

Word segmentation is the process of dividing a phrase without spaces back into its constituent parts. For example, consider a phrase like thisisatest. Humans can immediately identify that the correct phrase should be this is a test. But for machines, this is a tricky problem.

An approach to this problem

A basic idea would be to use a dictionary, and then to try to split words if the current chunk of letters is a valid word. But then you run into issues with sentences like peanutbutter that you will split with this approach as pea nut butter instead of peanut butter.

The idea was to take advantage of frequencies of words in a corpus. This is where the concept of a n-gram is used. In the fields of computational linguistics and probability, an n-gram is a contiguous sequence of n items from a given sequence of text or speech. The items can be phonemes, syllables, letters, words or base pairs according to the application.

For example, this is an extract of some unigrams in a corpus composed of 1,024,908,267,229 words distributed by the Linguistic Data Consortium.

used 421438139
go 421086358
b 419765694
work 419483948
last 417601616
most 416210411
music 414028837
buy 410780176
data 406908328
make 405084642
them 403000411
should 402028056

Using unigrams and bigrams, we can score an arrangement of words. This is what is done in the score method for example.

Concurrency and channels

This was also a great opportunity for me to work with channels, because some parts of the program can be run in parallel. I’m just starting to work around goroutines and channels, but I really like it!

Take a look at the source code and the documentation on GitHub: github.com/AntoineAugusti/wordsegmentation

Feature flags API in golang

Standard

Over the last few months, I’ve been interested in golang (the Go language) but I didn’t know what to build to really try it. Sure, I’ve done the exercises from the online tutorial and I’ve read the awesome website Go by example, but I didn’t have a real use-case yet. Until a few days ago when I decided to build an API related to feature flags!

What are feature flags?

Feature flags let you enable or disable some features of your application, for example when you’re under unexpected traffic or when you want to let some users try a new feature you’ve been working on. They decouple feature release and code deployment, so that you can release features whenever you want, instead of whenever the code happens to ship.

With this package, you can enable the access of a feature for:

  • specific user IDs
  • specific groups
  • a percentage of your user base
  • everyone
  • no one

And you can combine things! You can give access to a feature for users in the group dev or admin and for users 1337 and 42 if you want to.

What I’ve learned

I guess it’s a rather complete project because it involves a storage layer (a key-value store, with bolt), some logic around a simple model (what is a feature? How do we control access to a feature?) and an HTTP layer (with the default HTTP server and gorilla/mux). Moreover I’ve tried to write some tests, and it was really interesting to discover the “Go way” to do it!

Anyway, I’ve learned a lot and I’m fairly happy with the codebase, but if you spot anything that can be improved or that is wrong, please do get in touch with me (GitHub issues and tweets are perfect).

Here is the source code: github.com/AntoineAugusti/feature-flags.

Keeping my brain busy in my free time

Standard

For the last few months, I’ve tried to do sometimes in my free time what is often called “code katas”.

A code kata is an exercise in programming which helps a programmer hone their skills through practice and repetition.

Basically, you try to solve problems by writing algorithms. If you don’t know what to solve, there are a lot of platforms you can look at to find exercises:
CodinGame
Prologin (in French)
Rosalind (bioinformatics challenges)
Kaggle (machine learning competitions)

On most of these platforms, you can “play” alone. You are given a problem with detailed instructions, a dataset and an example of the expected output. Your goal will be to solve this problem. You will know if you have successfully solved the problem when you will submit your solution (usually you can use the language of your choice) thanks to automated unit tests.

I like to solve unusual problems, and most of the time they are more difficult than what I work on on a daily basis (at least when you’ve done enough exercices). This is a great opportunity to discover advanced algorithms or to learn a new language. Because you do this in your time it’s always nice to know than these problems can usually be solved in less than 1 hour, and also that you will not need to maintain your implementation in the future! When you’ve successfully solved a problem, you’re done for real 🙂

If you wanna check what I’ve already done, and maybe to see how I’ve done it, I’ve got a public GitHub repository dedicated to this: github.com/AntoineAugusti/katas.

Decorator pattern and repositories

Standard

My use case

Lately I’ve been using a lot the decorator pattern with repositories on Teen Quotes. My use case is somewhat simple: I use a relational database (MySQL) but sometimes I what to cache the results of some queries in a key-value store (Redis / Memcached for instance). With something like that, I don’t need to hit my database for queries that are always run or are slow to run, I’ll hit my key-value store instead. It’ll reduce the pressure on my database and will give some results faster for the application.

The decorator pattern

If you’re not familiar with the decorator pattern yet, it’s quite simple to use and I’m sure you’ll love it in no time. Basically, the decorator pattern allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. As Richard Bagshaw said, the idea is that you take an object, and you wrap this object in another object which provides additional functionality, and you keep wrapping extra classes repeatedly for each additional requirement.

If you want to see some real world examples, continue to read this blog post or go directly to Laracasts.

Some code

I’ll show you something I’ve been working on last week: the ability to add tags to quotes. A “tag” is like a category for a “quote” (a post, an article, whatever you want to call it). I’m using Laravel with Eloquent for my relational database. I’ve created an interface called TagRepository.

namespace TeenQuotes\Tags\Repositories;

use TeenQuotes\Tags\Models\Tag;
use TeenQuotes\Quotes\Models\Quote;

class DbTagRepository implements TagRepository {

  /**
   * Create a new tag
   *
   * @param  string $name
   * @return \TeenQuotes\Tags\Models\Tag
   */
  public function create($name)
  {
    return Tag::create(compact('name'));
  }

  /**
   * Get a tag thanks to its name
   *
   * @param  string $name
   * @return \TeenQuotes\Tags\Models\Tag|null
   */
  public function getByName($name)
  {
    return Tag::whereName($name)->first();
  }

  /**
   * Add a tag to a quote
   *
   * @param  \TeenQuotes\Quotes\Models\Quote $q
   * @param  \TeenQuotes\Tags\Models\Tag $t
   */
  public function tagQuote(Quote $q, Tag $t)
  {
    $q->tags()->attach($t);
  }

  /**
   * Remove a tag from a quote
   *
   * @param  \TeenQuotes\Quotes\Models\Quote $q
   * @param  \TeenQuotes\Tags\Models\Tag $t
   */
  public function untagQuote(Quote $q, Tag $t)
  {
    $q->tags()->detach($t);
  }

  /**
   * Get a list of tags for a given quote
   *
   * @param  \TeenQuotes\Quotes\Models\Quote $q
   * @return array
   */
  public function tagsForQuote(Quote $q)
  {
    return $q->tags()->lists('name');
  }

  /**
   * Get the total number of quotes having a tag
   *
   * @param  \TeenQuotes\Tags\Models\Tag $t
   * @return int
   */
  public function totalQuotesForTag(Tag $t)
  {
    return $t->quotes()->count();
  }
}

Pretty simple stuff, I’m sure you’ve seen this multiple times. Let’s move on to the interesting part: the caching layer. We will create a new class CachingTagRepository implementing the same interface TagRepository. The key thing is that we’ll require a TagRepository class to be given in the constructor of this new class. Ultimately, we will pass the DB layer here.

 
namespace TeenQuotes\Tags\Repositories;

use Cache;
use TeenQuotes\Tags\Models\Tag;
use TeenQuotes\Quotes\Models\Quote;

class CachingTagRepository implements TagRepository {

  /**
   * @var \TeenQuotes\Tags\Repositories\TagRepository
   */
  private $tags;

  public function __construct(TagRepository $tags)
  {
    // The key thing is here: we assume we've already
    // a class that is implementing the interface.
    // We can rely on that!
    $this->tags = $tags;
  }

  /**
   * Create a new tag
   *
   * @param  string $name
   * @return \TeenQuotes\Tags\Models\Tag
   */
  public function create($name)
  {
    return $this->tags->create($name);
  }

  /**
   * Get a tag thanks to its name
   *
   * @param  string $name
   * @return \TeenQuotes\Tags\Models\Tag|null
   */
  public function getByName($name)
  {
    $callback = function() use ($name)
    {
      return $this->tags->getByName($name);
    };

    return Cache::rememberForever('tags.name-'.$name, $callback);
  }

  /**
   * Add a tag to a quote
   *
   * @param  \TeenQuotes\Quotes\Models\Quote $q
   * @param  \TeenQuotes\Tags\Models\Tag $t
   */
  public function tagQuote(Quote $q, Tag $t)
  {
    Cache::forget($this->cacheNameForListTags($q));

    $keyTotal = $this->cacheNameTotalQuotesForTag($t);

    if (Cache::has($keyTotal))
      Cache::increment($keyTotal);

    return $this->tags->tagQuote($q, $t);
  }

  /**
   * Remove a tag from a quote
   *
   * @param  \TeenQuotes\Quotes\Models\Quote $q
   * @param  \TeenQuotes\Tags\Models\Tag $t
   */
  public function untagQuote(Quote $q, Tag $t)
  {
    Cache::forget($this->cacheNameForListTags($q));

    $keyTotal = $this->cacheNameTotalQuotesForTag($t);

    if (Cache::has($keyTotal))
      Cache::decrement($keyTotal);

    return $this->tags->untagQuote($q, $t);
  }

  /**
   * Get a list of tags for a given quote
   *
   * @param  \TeenQuotes\Quotes\Models\Quote $q
   * @return array
   */
  public function tagsForQuote(Quote $q)
  {
    $key = $this->cacheNameForListTags($q);

    $callback = function() use($q)
    {
      return $this->tags->tagsForQuote($q);
    };

    return Cache::remember($key, 10, $callback);
  }

  /**
   * Get the total number of quotes having a tag
   *
   * @param  \TeenQuotes\Tags\Models\Tag $t
   * @return int
   */
  public function totalQuotesForTag(Tag $t)
  {
    $key = $this->cacheNameTotalQuotesForTag($t);

    $callback = function() use ($t)
    {
      return $this->tags->totalQuotesForTag($t);
    };

    return Cache::remember($key, 10, $callback);
  }

  /**
   * Get the key name when we list tags for a quote
   *
   * @param  \TeenQuotes\Quotes\Models\Quote $q
   * @return string
   */
  private function cacheNameForListTags(Quote $q)
  {
    return 'tags.quote-'.$q->id.'.list-name';
  }

  /**
   * Get the key name to have the number of quotes
   * having a tag
   *
   * @param  \TeenQuotes\Tags\Models\Tag $t
   * @return string
   */
  private function cacheNameTotalQuotesForTag(Tag $t)
  {
    return 'tags.tag-'.$t->name.'.total-quotes';
  }
}

You see, we do some things before (or after) calling the initial implementation, to add some functionalities (here a caching layer). Sometimes we directly defer to the initial implementation (see the create method).

Bonus: registering that in the IoC container

Let’s bind our TagRepository interface to the caching layer and the storage layer in a service provider!

namespace TeenQuotes\Tags;

use Illuminate\Support\ServiceProvider;
use TeenQuotes\Tags\Repositories\CachingTagRepository;
use TeenQuotes\Tags\Repositories\DbTagRepository;
use TeenQuotes\Tags\Repositories\TagRepository;

class TagsServiceProvider extends ServiceProvider {

  /**
   * Bootstrap the application events.
   *
   * @return void
   */
  public function boot()
  {
      //
  }

  /**
   * Register the service provider.
   *
   * @return void
   */
  public function register()
  {
      $this->registerBindings();
  }

  private function registerBindings()
  {
      $this->app->bind(TagRepository::class, function()
      {
          return new CachingTagRepository(new DbTagRepository);
      });
  }
}

Et voilà ! Happy coding!

Using JavaScript / jQuery to detect if someone is using AdBlock

Standard

Let’s face it: ads are not cool. I know, but sometimes you have no other choice than to use them to pay your server / your coffee / whatever. And if you are using ads, you know that some people will not see your ads because they are using a plugin that blocks ads on your website. You want to reach these visitors and just say

“Hey, we know ads are not cool, but please make an exception for us or give us a small amount of money, just to support our work”

On Teen Quotes I am showing a friendly message in place of the only ad to say what I have to say. Here is what people with an ad blocker are seeing:
adblock

But let’s get back to the point: detect visitors that are using an ad blocker with JavaScript.

Some code

Let’s write some code. You’re going to write the most simple JS file you have ever written. Ready? Here it is:

var isNaughtyVisitor = false;

BOOM! That was fast, isn’t it? In fact, we don’t care about the variable name or its value, we just want to define a new variable. But this variable needs to be defined in a file called ads.js that you will include in your HTML. Why so? This is were it gets funny. Ad blockers are a little dumb, so when they see a file named something like ad.js or ads.js, they will block the request. And if the request was blocked… your variable will not be defined!

The second step is just to test in your regular JS file of your application if the variable was defined. This file needs to be include after the previous file, ads.js. You can add something like this:

$(document).ready(function() {
    // The div that will add to the DOM if the visitor is 
    // using an ad blocker
    var div = 'Your amazing HTML block here';
    
    // The script was never called, probably using an ad blocker
    if (typeof(isNaughtyVisitor) == "undefined") {
        // Insert the div wherever you want
        $("#footer").before(div);
        // Send the event to Google Analytics if you want to track
        ga('send', 'event', 'ads', 'hidden');
    // A friendly user!
    } else {
        // Send the event to Google Analytics if you want to track
        ga('send', 'event', 'ads', 'displayed');
    }
});

Pretty darn simple, and really effective. The downside is that friendly users will have to make an extra HTTP request. Yep, I know, it’s not fair for them.

Using ImageMagick to crop images

Standard

ImageMagick is a PHP library for image processing and image generation. It is a requirement for a popular library, stojg/crop that offers various cropping techniques. The library can be installed easily via Composer, but it requires ImageMagick to work.

Installing ImageMagick on Ubuntu / Debian

You are lucky, there is a package for that. It is just a one line command:

sudo apt-get install php5-imagick

It will grab ImageMagick, build the PHP extension and enable it in your php.ini file. You may have to restart your web server or PHP-FPM to reflect the changes.

Using ImageMagick on Travis CI

If you are running a continuous integration test suite, you may be interested to test some methods that are using the ImageMagick library under the hood. In order to do that, the ImageMagick library needs to be installed on the Travis’ VM. At the time, the ImageMagick library is already pulled in for every VM, but the PHP extension is not enabled by default. We will still try to grab the library before enabling it, just to be cautious. You’ll have to edit your travis.yml file and add the following lines to your before_script:

before_script:
  - sudo apt-get update -q
  - sudo apt-get install -y imagemagick
  - printf "\n" | pecl install imagick

The last line is a very dirty hack: when running the command pecl install imagick, you have to hit “Enter” to autodetect some settings. That’s why you need to pipe a new line to this command. Dirty, but it works, you’ll be able to test methods using ImageMagick afterwards.

Laravel package for the recommendation system Easyrec

Standard

During a few days in August I have coded a Laravel wrapper for the recommendation system Easyrec. If you want to display most viewed items, best rated items, related items or something like that, a recommendation system is the perfect way to go. If you are familiar with Machine Learning techniques, you now that a recommendation system is something very difficult. If you remember, Netflix offered $ 1,000,000 to improve its collaborative filtering algorithm.

Easyrec provides a REST API that you can call for free. This is something very convenient if don’t have a lot of data and that you still want to use a recommender system for your web service.

Features overview

After registering the service provider and the alias in your app/config/app.php file, it will be super easy to use! Here is a little overview of the available functions:

Easyrec::view(42, 'Post 42', 'http://example.com/posts/42', 200, null, null, 'POST'); // User #200 has viewed post #42
Easyrec::rate(42, 8, 'Book 42', 'http://example.com/books/42', 200, null, null, 'BOOK'); // User #200 has rated 8/10 book #42
Easyrec::mostViewedItems(10, 'WEEK', 'BOOK'); // Retrieves the 10 most viewed books for the last week
Easyrec::bestRatedItems(10, 'MONTH', 'POST'); // Retrieves the 10 best rated posts for the last month

Documentation and download

Of course the package is available via Composer. The full documentation can be found on GitHub: github.com/AntoineAugusti/laravel-easyrec.

Do not hesitate to open up an issue if something is not working as expected. Pull-requests are very welcome also!

Error handlers order in Laravel

Standard

Error handlers are a neat feature of Laravel. They allow you to deal with custom exceptions so that you know what to do when something wrong happens. If you want to play with error handlers, you need to know two vital things:

  1. If an exception handler returns a response, that response will be sent to the browser and no other error handlers will be called ;
  2. Handlers are called based on the type-hint of the Exception they handle. If two handlers handle the same exception, the last one will be called first.

About error handlers order

An example:

App::error(function(MyCustomException $exception, $code)
{
	// This handler will not be called first
});

App::error(function(MyCustomException $exception, $code)
{
	// This handler will be called first
});

Error handlers and responses

If your last handler returns a response, your previous handler will not be called because of the first rule.

App::error(function(MyCustomException $exception, $code)
{
	// This handler will not be called AT ALL
});

App::error(function(MyCustomException $exception, $code)
{
	// This handler will be called first
	$data = ['title' => 'Error occured'];

	// We are returning a response: no other error handlers will be called
	return Response::view('errors.default', $data, $code);
});

Playing with error handlers order

Let’s say you want an error handler to be called at the bottom of the stack. This is very useful for example if you are using a package that registers an error handler and you want one of your error handlers to be called after. Laravel provides the App::pushError() function that registers an error handler at the bottom of the stack.

App::error(function(MyCustomException $exception, $code)
{
	// This handler will be called first
	// If we are returning a response here, no other error handlers will be called
});

App::pushError(function(MyCustomException $exception, $code)
{
	// This handler will be called at the end
	$data = ['title' => 'Error occured'];

	return Response::view('errors.default', $data, $code);
});

Inlining CSS when sending an email with Mailgun in Laravel

Standard

Since Laravel 4.2, it is possible to use external emails providers to send emails in your application: Mailgun and Mandrill. Before that I was using a nice plugin fedeisas/laravel-mail-css-inliner to inline CSS just before sending the email. Thanks to this, my views are very clean and my emails are still displayed properly in the various email clients and webmails. This plugin was taking advantage of SwiftMailer to inline the CSS when sending an email by registering a plugin. Unfortunately, it is not working with external providers because SwiftMailer is not used since an API call is made instead.

Extending some classes to fix this

I really wanted to inline my CSS before sending an email and I wanted a clean way to do this. A workaround that I have figured out is to extend two classes: Illuminate\Mail\MailServiceProvider and Illuminate\Mail\Transport\MailgunTransport.

I’ve created a new file located at app/lib/TeenQuotes/Mail/Transport/MailgunTransport.php. The goal was to edit the message before calling the Mailgun API.

namespace TeenQuotes\Mail\Transport;

use Swift_Transport;
use Swift_Mime_Message;
use GuzzleHttp\Post\PostFile;
use Swift_Events_EventListener;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;

class MailgunTransport extends \Illuminate\Mail\Transport\MailgunTransport {

	/**
	 * {@inheritdoc}
	 */
	public function send(Swift_Mime_Message $message, &$failedRecipients = null)
	{
		$client = $this->getHttpClient();

		// Inline CSS here
		$converter = new CssToInlineStyles();
		$converter->setEncoding($message->getCharset());
		$converter->setUseInlineStylesBlock();
		$converter->setCleanup();

		if ($message->getContentType() === 'text/html' ||
			($message->getContentType() === 'multipart/alternative' && $message->getBody())
		) {
			$converter->setHTML($message->getBody());
			$message->setBody($converter->convert());
		}

		foreach ($message->getChildren() as $part) {
			if (strpos($part->getContentType(), 'text/html') === 0) {
				$converter->setHTML($part->getBody());
				$part->setBody($converter->convert());
			}
		}

		// Call the API
		$client->post($this->url, ['auth' => ['api', $this->key],
			'body' => [
				'to' => $this->getTo($message),
				'message' => new PostFile('message', (string) $message),
			],
		]);
	}
}

Since we have a new MailgunTransport, we need to use our custom MailgunTransport when sending an email. I have created a new file at app/lib/TeenQuotes/Mail/MailServiceProvider.php.

namespace TeenQuotes\Mail;

use TeenQuotes\Mail\Transport\MailgunTransport;

class MailServiceProvider extends \Illuminate\Mail\MailServiceProvider {

	/**
	 * Register the Mailgun Swift Transport instance.
	 *
	 * @param  array  $config
	 * @return void
	 */
	protected function registerMailgunTransport($config)
	{
		$mailgun = $this->app['config']->get('services.mailgun', array());

		$this->app->bindShared('swift.transport', function() use ($mailgun)
		{
			return new MailgunTransport($mailgun['secret'], $mailgun['domain']);
		});
	}
}

Not so much work, I just use my custom MailgunTransport that I have just created.

Replacing the Mail Provider

You need to update providers in app/config/app.php to replace the MailServiceProvider with our custom provider.

	'providers' => array(

		// Some others providers...
		'Illuminate\Log\LogServiceProvider',
		// Comment this 'Illuminate\Mail\MailServiceProvider', 
		// We add our new MailServiceProvider
		'TeenQuotes\Mail\MailServiceProvider',
		// Some more providers...
	),

Updating composer.json

We need some new plugins

	"require": {
		// Your plugins
		"tijsverkoyen/css-to-inline-styles": "1.2.*",
		"guzzlehttp/guzzle": "~4.0"
	},

And we need to update the autoload section to be able to load our custom library

	"autoload": {
		"classmap": [
			"app/commands",
			"app/controllers",
			"app/models",
			"app/database/migrations",
			"app/database/seeds",
			"app/exceptions.php",
			"app/tests/TestCase.php"
		],
		"psr-0": {
			"TeenQuotes": "app/lib"
		}
	},

A simple composer dump-autoload and you will be good! Do not forget to set your API key and your mail domain for Mailgun in app/config/services.php.

If you want to use a different namespace of course you are free!

Laravel: fulltext selection and ordering

Standard

Yesterday I was looking for a way to do a FULLTEXT select using Laravel. It was not so easy. In this article I’m going to explain how to a FULLTEXT select and to order by this selection.

The migration

If you want to do a FULLTEXT search, you will need a FULLTEXT index on at least one column of your table. Warning: if you are using InnoDB as your table’s engine, you will need MySQL >= 5.6. If you are using MyISAM as your table’s engine, you are good to go for the index but you can’t use foreign keys.

I’m using InnoDB with MySQL 5.6, here is my code for the migration of the table.

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateQuotesTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::dropIfExists('quotes');

        Schema::create('quotes', function(Blueprint $table) {
            $table->engine = "InnoDB";
            $table->increments('id');
            $table->string('content', 500);
            $table->integer('user_id')->unsigned()->index();
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->tinyInteger('approved')->default(0);
            $table->timestamps();
        });

        // Here we create the FULLTEXT index
        DB::statement('ALTER TABLE quotes ADD FULLTEXT search(content)');
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        // Drop the index before dropping the table
        Schema::table('quotes', function($table) {
            $table->dropIndex('search');
        });
        Schema::drop('quotes');
    }

}

Nothing uncommon, note that you will have to use a DB::statement('ALTER TABLE quotes ADD FULLTEXT search(content)') to create the index.

Searching using the FULLTEXT index

Here it comes the fun part. Now that we have your index, let’s begin to use it. I want to get quotes based on a search on their content. I want pertinent results so I’ll advantage of the index.

My code is the following:

/**
 * @brief Function used to search for quotes using the FULLTEXT index on content
 *
 * @param  string $search Our search query
 * @return Collection Collection of Quote
 */
public static function searchQuotes($search)
{
    return Quote::
    select('id', 'content', 'user_id', 'approved', 'created_at', 'updated_at', DB::raw("MATCH(content) AGAINST(?) AS `rank`"))
    // $search will NOT be bind here
    // it will be bind when calling setBindings
    ->whereRaw("MATCH(content) AGAINST(?)", array($search))
    // I want to keep only published quotes
    ->where('approved', '=', 1)
    // Order by the rank column we got with our FULLTEXT index
    ->orderBy('rank', 'DESC')
    // Bind variables here
    // We really need to bind ALL variables here
    // question marks will be replaced in the query
    ->setBindings([$search, $search, 1])
    ->get();
}

I haven’t found a convenient way to select all columns from my table plus an additional one: the rank given by the FULLTEXT search. The tricky part here is really the binding. You need to bind all variables at the end of your query to make it work.

I’m not using the FULLTEXT search in BOOLEAN MODE here. If you need to do so, take a look at the official documentation: http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html. You will only need to add two strings to make it work.