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.